package com.unionpay.fsas.encrypt.encoder;

import com.unionpay.fsas.encrypt.encoder.connect.UnionComm;
import com.unionpay.fsas.encrypt.encoder.connect.UnionStart;
import com.unionpay.fsas.encrypt.encoder.message.UnionMessage;
import com.unionpay.fsas.encrypt.encoder.message.UnionRequest;
import com.unionpay.fsas.encrypt.encoder.utils.Base64;
import com.unionpay.fsas.encrypt.encoder.utils.UnionStr;
import com.unionpay.fsas.encrypt.encoder.utils.UnionUtil;
import com.unionpay.fsas.encrypt.encoder.utils.UnionVerify;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * ESSC服务封装demo
 *
 * @author linxl
 */
public class UnionEsscDemo {

    /**
     * 开始初始化
     */
    private static UnionStart api;

    public UnionEsscDemo(UnionStart unionStrart) {
        UnionEsscDemo.api = unionStrart;
    }

    /**
     * EER1 贵阳银行控件转加密
     * keyName	密钥名称，转加密原始SM4密钥值
     * keyByPk	SM2公钥加密的zpk
     * pinBlock	源PIN块，源zpk加密的字符密码密文，pin明文最长24个字符
     * zpkName	目的zpk
     * srcPan	源帐号
     * dstPan	目的帐号
     */
    public UnionMessage servEFR1(String keyName, String keyByPk, String pinBlock,
                                 String zpkName, String srcPan, String dstPan) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(keyByPk)) {
            return UnionVerify.paramValueWrong("keyByPk");
        }

        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }

        if (UnionVerify.paramIsEmpty(zpkName)) {
            return UnionVerify.paramValueWrong("zpkName");
        }

        if (UnionVerify.paramIsEmpty(srcPan)) {
            return UnionVerify.paramValueWrong("srcPan");
        }

        if (UnionVerify.paramIsEmpty(dstPan)) {
            return UnionVerify.paramValueWrong("dstPan");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EFR1");
        um.putBodyField("keyName", keyName);
        um.putBodyField("keyByPk", keyByPk);
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("zpkName", zpkName);
        um.putBodyField("srcPan", srcPan);
        um.putBodyField("dstPan", dstPan);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EFR2  贵阳银行PIN转加密（私有算法和标准算法互转）
     * mode 模式  1：标准pin加密算法（ansi9.8 01格式）转云贵阳银行私有算法  2：贵阳银行私有算法转标准pin加密算法（ansi9.8 01格式）
     * keyName	ZPK密钥名称	用于保护标准pin格式的明文，不对贵阳银行私有算法密文进行保护  LMK对（06-07）下加密
     * oriPIN	源PIN块
     * sSeed	加密种子
     * accNo	账号
     */
    public UnionMessage servEFR2(int mode, String keyName, String oriPIN, String sSeed,
                                 String accNo) {

        if (mode < 1 || mode > 2) {
            return UnionVerify.paramValueWrong("mode");
        }

        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        if (UnionVerify.paramIsEmpty(oriPIN)) {
            return UnionVerify.paramValueWrong("oriPIN");
        }

        if (UnionVerify.paramIsEmpty(sSeed)) {
            return UnionVerify.paramValueWrong("sSeed");
        }

        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }


        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EFR2");
        um.putBodyField("mode", mode);
        um.putBodyField("keyName", keyName);
        um.putBodyField("oriPIN", oriPIN);
        um.putBodyField("sSeed", sSeed);
        um.putBodyField("accNo", accNo);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E100 测试密码机状态
     *
     * @param hsmIP 密码机IP，可选
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE100(String hsmIP) {

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E100");

        //组装报文体
        //判断可选
        if (!UnionVerify.paramIsEmpty(hsmIP)) {
            um.putBodyField("hsmIP", hsmIP);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E101 执行密码机指令
     *
     * @param hsmCmdReq    请求指令
     * @param ascFlag      编码标识
     * @param lenOfMsgHead 消息头长度
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = hsmCmdRes} 	：响应指令
     */
    public UnionMessage servE101(String hsmCmdReq, String ascFlag, String lenOfMsgHead) {
        if (UnionVerify.paramIsEmpty(hsmCmdReq)) {
            return UnionVerify.paramValueWrong("hsmCmdReq");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E101");

        //组装报文体
        um.putBodyField("hsmCmdReq", hsmCmdReq);

        //判断可选
        if (!UnionVerify.paramIsEmpty(ascFlag)) {
            um.putBodyField("ascFlag", ascFlag);
        }
        if (!UnionVerify.paramIsEmpty(lenOfMsgHead)) {
            um.putBodyField("lenOfMsgHead", lenOfMsgHead);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E110 生成对称密钥
     *
     * @param keyName               密钥名称，可选
     * @param keyGroup              密钥组，可选
     * @param algorithmID           算法标识
     * @param keyType               密钥类型
     * @param keyLen                密钥长度
     * @param updateKeyFlag         更新密钥标识，可选
     * @param oldVersionKeyIsUsed   允许使用旧密钥，可选
     * @param inputFlag             允许导入标识，可选
     * @param outputFlag            允许导出标识，可选
     * @param effectiveDays         有效天数，可选
     * @param enabled               启用标识，可选
     * @param activeDate            生效日期，可选
     * @param keyApplyPlatform      密钥申请平台，可选
     * @param keyDistributePlatform 密钥分发平台，可选
     * @param mode                  模式，可选
     * @param exportFlag            输出标识，可选
     * @param exportLmkKey          输出标识，可选
     * @param protectKey            保护密钥，可选
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = keyValue} 	：密钥密文；
     * {@code string = checkValue} 	：密钥校验值；
     * {@code string = keyValue2} 	：密钥密文；
     */
    public UnionMessage servE110(String keyName, String keyGroup, String algorithmID, String keyType, String keyLen,
                                 String updateKeyFlag, String oldVersionKeyIsUsed, String inputFlag, String outputFlag, String effectiveDays, String enabled, String activeDate,
                                 String keyApplyPlatform, String keyDistributePlatform, String mode, String exportFlag, String exportLmkKey, String protectKey) {

        //判断必填项
        if (UnionVerify.paramIsEmpty(algorithmID)) {
            return UnionVerify.paramValueWrong("algorithmID");
        }
        if (UnionVerify.paramIsEmpty(keyType)) {
            return UnionVerify.paramValueWrong("keyType");
        }
        if (UnionVerify.paramIsEmpty(keyLen)) {
            return UnionVerify.paramValueWrong("keyLen");
        }
        if (!"64".equals(keyLen) && !"128".equals(keyLen) && !"192".equals(keyLen) && !("256".equals(keyLen) && "AES".equals(algorithmID))) {
            return UnionVerify.paramValueWrong("keyLen");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E110");

        //组装报文体
        um.putBodyField("algorithmID", algorithmID);
        um.putBodyField("keyType", keyType);
        um.putBodyField("keyLen", keyLen);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(keyGroup)) {
            um.putBodyField("keyGroup", keyGroup);
        }
        if (!UnionVerify.paramIsEmpty(updateKeyFlag)) {
            um.putBodyField("updateKeyFlag", updateKeyFlag);
        }
        if (!UnionVerify.paramIsEmpty(oldVersionKeyIsUsed)) {
            um.putBodyField("oldVersionKeyIsUsed", oldVersionKeyIsUsed);
        }
        if (!UnionVerify.paramIsEmpty(inputFlag)) {
            um.putBodyField("inputFlag", inputFlag);
        }
        if (!UnionVerify.paramIsEmpty(outputFlag)) {
            um.putBodyField("outputFlag", outputFlag);
        }
        if (!UnionVerify.paramIsEmpty(effectiveDays)) {
            um.putBodyField("effectiveDays", effectiveDays);
        }
        if (!UnionVerify.paramIsEmpty(enabled)) {
            um.putBodyField("enabled", enabled);
        }
        if (!UnionVerify.paramIsEmpty(activeDate)) {
            um.putBodyField("activeDate", activeDate);
        }
        if (!UnionVerify.paramIsEmpty(keyApplyPlatform)) {
            um.putBodyField("keyApplyPlatform", keyApplyPlatform);
        }
        if (!UnionVerify.paramIsEmpty(keyDistributePlatform)) {
            um.putBodyField("keyDistributePlatform", keyDistributePlatform);
        }
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }

        if (!UnionVerify.paramIsEmpty(exportFlag)) {
            um.putBodyField("exportFlag", exportFlag);
        }
        if (!UnionVerify.paramIsEmpty(exportLmkKey)) {
            um.putBodyField("exportLmkKey", exportLmkKey);
        }
        if (!UnionVerify.paramIsEmpty(protectKey)) {
            um.putBodyField("protectKey", protectKey);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E111 更新对称密钥
     *
     * @param keyName      密钥名称
     * @param mode         模式，可选
     * @param exportFlag   输出标识，可选
     * @param exportLmkKey 输出标识，可选
     * @param protectKey   保护密钥，条件存在
     * @param activeDate   生效日期，可选
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = keyValue} 	：密钥密文，指定保护密钥保护的密钥密文；
     * {@code string = checkValue} 	：密钥校验值；
     * {@code string = keyValue2} 	：密钥密文，lmk保护的密钥密文,当exportLmkKey为1时存在；
     */
    public UnionMessage servE111(String keyName, String mode, String exportFlag, String exportLmkKey, String protectKey, String activeDate) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E111");
        //组装报文体
        um.putBodyField("keyName", keyName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(exportFlag)) {
            um.putBodyField("exportFlag", exportFlag);
        }
        if (!UnionVerify.paramIsEmpty(exportLmkKey)) {
            um.putBodyField("exportLmkKey", exportLmkKey);
        }
        if (!UnionVerify.paramIsEmpty(protectKey)) {
            um.putBodyField("protectKey", protectKey);
        }
        if (!UnionVerify.paramIsEmpty(activeDate)) {
            um.putBodyField("activeDate", activeDate);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * @param keyName     密钥名称
     * @param keyValue    密钥密文
     * @param checkValue  密钥校验值，可选
     * @param protectFlag 保护方式
     * @param protectKey  保护密钥
     * @param activeDate  生效日期，可选
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = checkValue} 	：密钥校验值
     */
    public UnionMessage servE112(String keyName, String keyValue, String checkValue, String protectFlag, String protectKey, String activeDate) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(keyValue)) {
            return UnionVerify.paramValueWrong("keyValue");
        }
        if (UnionVerify.paramIsEmpty(protectFlag)) {
            return UnionVerify.paramValueWrong("protectFlag");
        }
        if (UnionVerify.paramIsEmpty(protectKey)) {
            return UnionVerify.paramValueWrong("protectKey");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E112");
        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("keyValue", keyValue);
        um.putBodyField("protectFlag", protectFlag);
        um.putBodyField("protectKey", protectKey);

        //判断可选
        if (!UnionVerify.paramIsEmpty(checkValue)) {
            um.putBodyField("checkValue", checkValue);
        }
        if (!UnionVerify.paramIsEmpty(activeDate)) {
            um.putBodyField("activeDate", activeDate);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E113 导出对称密钥
     *
     * @param keyName      密钥名称
     * @param protectFlag  保护方式
     * @param exportLmkKey 输出标识
     * @param protectKey   保护密钥
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = keyValue} 	：密钥密文；
     * {@code string = checkValue} 	：密钥校验值；
     * {@code string = keyValue2} 	：密钥密文；
     */
    public UnionMessage servE113(String keyName, String protectFlag, String exportLmkKey, String protectKey) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(protectFlag)) {
            return UnionVerify.paramValueWrong("protectFlag");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E113");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("protectFlag", protectFlag);

        //判断可选
        if (!UnionVerify.paramIsEmpty(exportLmkKey)) {
            um.putBodyField("exportLmkKey", exportLmkKey);
        }
        if (!UnionVerify.paramIsEmpty(protectKey) && !"9".equals(protectFlag)) {
            um.putBodyField("protectKey", protectKey);
        } else {
            return UnionVerify.paramValueWrong("protectKey");
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E114 申请对称密钥
     *
     * @param keyName 密钥名称
     * @param mode    模式
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE114(String keyName, String mode) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E114");

        //组装报文体
        um.putBodyField("keyName", keyName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E115 分发对称密钥
     *
     * @param keyName 密钥名称
     * @param mode    模式
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE115(String keyName, String mode) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E115");

        //组装报文体
        um.putBodyField("keyName", keyName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E116 销毁对称密钥
     *
     * @param keyName 密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE116(String keyName) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E116");

        //组装报文体
        um.putBodyField("keyName", keyName);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E117 获取对称密钥信息
     *
     * @param keyName 密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = keyGroup} 	：密钥组；
     * {@code string = algorithmID} 	：算法标识；
     * {@code string = keyType} 	：密钥类型；
     * {@code string = keyLen} 	：密钥长度；
     * {@code string = oldVersionKeyIsUsed} 	：允许使用旧密钥；
     * {@code string = inputFlag} 	：允许导入标识；
     * {@code string = outputFlag} 	：允许导出标识；
     * {@code string = effectiveDays} 	：有效天数；
     * {@code string = activeDate} 	：生效日期；
     * {@code string = status} 	：状态；
     * {@code string = creatorType} 	：创建者类型；
     * {@code string = creator} 	：创建者；
     * {@code string = createTime} 	：创建时间；
     * {@code string = keyUpdateTime} 	：密钥更新时间；
     * {@code string = keyApplyPlatform} 	：密钥申请平台；
     * {@code string = keyDistributePlatform} 	：密钥分发平台；
     * {@code string = usingUnit} 	：使用单位；
     * {@code string = remark} 	：备注。
     */
    public UnionMessage servE117(String keyName) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E117");

        //组装报文体
        um.putBodyField("keyName", keyName);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E118 打印对称密钥
     *
     * @param mode              打印模式
     * @param keyName           密钥名称
     * @param algorithmID       算法标识
     * @param keyLen            密钥长度
     * @param keyType           密钥类型
     * @param hsmIP             密码机IP
     * @param numOfComponent    分量数量
     * @param formatName        打印格式名称
     * @param appendPrintNum    附加打印数量
     * @param appendPrintParam1 附加打印参数值
     * @param appendPrintParam2 附加打印参数值
     * @param …                 …(参数值数量等于附加打印数量)
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE118(String mode, String keyName, String algorithmID, String keyLen,
                                 String keyType, String hsmIP, String numOfComponent, String formatName, String appendPrintNum, String appendPrintParam1, String appendPrintParam2) {
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(hsmIP)) {
            return UnionVerify.paramValueWrong("hsmIP");
        }
        if (UnionVerify.paramIsEmpty(numOfComponent)) {
            return UnionVerify.paramValueWrong("numOfComponent");
        }
        if (UnionVerify.paramIsEmpty(formatName)) {
            return UnionVerify.paramValueWrong("formatName");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E118");

        //组装报文体
        um.putBodyField("mode", mode);
        um.putBodyField("keyName", keyName);
        um.putBodyField("hsmIP", hsmIP);
        um.putBodyField("numOfComponent", numOfComponent);
        um.putBodyField("formatName", formatName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(algorithmID) && ("0".equals(mode) || "3".equals(mode))) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(keyLen) && ("0".equals(mode) || "3".equals(mode))) {
            um.putBodyField("keyLen", keyLen);
        }
        if (!UnionVerify.paramIsEmpty(keyType) && "0".equals(mode)) {
            um.putBodyField("keyType", keyType);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E119 启用、挂起对称密钥
     *
     * @param keyName 密钥名称
     * @param mode    模式  1：启用密钥  2：挂起密钥
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE119(String keyName, int mode) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (mode < 0 || mode > 2) {
            return UnionVerify.paramValueWrong("mode");
        }
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E119");
        um.putBodyField("keyName", keyName);
        um.putBodyField("mode", mode);
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E11A 密钥回退
     *
     * @param keyName 密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE11A(String keyName) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E11A");

        //组装报文体
        um.putBodyField("keyName", keyName);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E11B 约定传输密钥
     *
     * @param branchNo   分行号
     * @param rsaPKValue rsa公钥值
     * @param sm2PKValue sm2公钥值
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = desKeyByPK} 	：rsa加密的密钥值；
     * {@code string = desCheckValue} 	：rsa加密的校验值；
     * {@code string = sm4KeyByPK} 	：sm2加密的密钥值；
     * {@code string = sm4CheckValue} 	：sm2加密的校验值。
     */
    public UnionMessage servE11B(String branchNo, String rsaPKValue, String sm2PKValue) {
        if (UnionVerify.paramIsEmpty(branchNo)) {
            return UnionVerify.paramValueWrong("branchNo");
        }
        if (UnionVerify.paramIsEmpty(rsaPKValue)) {
            return UnionVerify.paramValueWrong("rsaPKValue");
        }
        if (UnionVerify.paramIsEmpty(sm2PKValue)) {
            return UnionVerify.paramValueWrong("sm2PKValue");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E11B");

        //组装报文体
        um.putBodyField("branchNo", branchNo);
        um.putBodyField("rsaPKValue", rsaPKValue);
        um.putBodyField("sm2PKValue", sm2PKValue);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E120 生成非对称密钥
     *
     * @param keyName               密钥名称
     * @param keyGroup              密钥组
     * @param algorithmID           算法标识
     * @param keyType               密钥类型
     * @param pkExponent            公钥指数
     * @param keyLen                密钥长度
     * @param updateKeyFlag         更新密钥标识
     * @param vkStoreLocation       私钥存储位置
     * @param hsmGroupID            密码机组名
     * @param vkIndex               私钥索引
     * @param oldVersionKeyIsUsed   允许使用旧密钥
     * @param mode                  模式
     * @param inputFlag             私钥允许导入标识
     * @param outputFlag            私钥允许导出标识
     * @param exportFlag            私钥输出标识
     * @param effectiveDays         有效天数
     * @param enabled               启用标识
     * @param activeDate            生效日期
     * @param keyApplyPlatform      密钥申请平台
     * @param keyDistributePlatform 密钥分发平台
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pkValue} 	：公钥值；
     * {@code string = vkValue} 	：私钥值。
     */
    public UnionMessage servE120(String keyName, String keyGroup, String algorithmID, String keyType,
                                 String pkExponent, String keyLen, String updateKeyFlag, String vkStoreLocation, String hsmGroupID, String vkIndex,
                                 String oldVersionKeyIsUsed, String mode, String inputFlag, String outputFlag, String exportFlag, String effectiveDays,
                                 String enabled, String activeDate, String keyApplyPlatform, String keyDistributePlatform) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(algorithmID)) {
            return UnionVerify.paramValueWrong("algorithmID");
        }
        if (UnionVerify.paramIsEmpty(keyType)) {
            return UnionVerify.paramValueWrong("keyType");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E120");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("algorithmID", algorithmID);
        um.putBodyField("keyType", keyType);

        //判断可选
        if (!UnionVerify.paramIsEmpty(keyGroup)) {
            um.putBodyField("keyGroup", keyGroup);
        }
        if (!UnionVerify.paramIsEmpty(pkExponent) && "RSA".equals(algorithmID)) {
            um.putBodyField("pkExponent", pkExponent);
        }
        if (!UnionVerify.paramIsEmpty(keyLen) && "RSA".equals(algorithmID)) {
            um.putBodyField("keyLen", keyLen);
        }
        if (!UnionVerify.paramIsEmpty(updateKeyFlag)) {
            um.putBodyField("updateKeyFlag", updateKeyFlag);
        }
        if (!UnionVerify.paramIsEmpty(vkStoreLocation)) {
            um.putBodyField("vkStoreLocation", vkStoreLocation);
        }
        if (!UnionVerify.paramIsEmpty(hsmGroupID)) {
            um.putBodyField("hsmGroupID", hsmGroupID);
        }
        if (!UnionVerify.paramIsEmpty(vkIndex)) {
            um.putBodyField("vkIndex", vkIndex);
        }
        if (!UnionVerify.paramIsEmpty(oldVersionKeyIsUsed)) {
            um.putBodyField("oldVersionKeyIsUsed", oldVersionKeyIsUsed);
        }
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(inputFlag)) {
            um.putBodyField("inputFlag", inputFlag);
        }
        if (!UnionVerify.paramIsEmpty(outputFlag)) {
            um.putBodyField("outputFlag", outputFlag);
        }
        if (!UnionVerify.paramIsEmpty(exportFlag)) {
            um.putBodyField("exportFlag", exportFlag);
        }
        if (!UnionVerify.paramIsEmpty(effectiveDays)) {
            um.putBodyField("effectiveDays", effectiveDays);
        }
        if (!UnionVerify.paramIsEmpty(enabled)) {
            um.putBodyField("enabled", enabled);
        }
        if (!UnionVerify.paramIsEmpty(activeDate)) {
            um.putBodyField("activeDate", activeDate);
        }
        if (!UnionVerify.paramIsEmpty(keyApplyPlatform)) {
            um.putBodyField("keyApplyPlatform", keyApplyPlatform);
        }
        if (!UnionVerify.paramIsEmpty(keyDistributePlatform)) {
            um.putBodyField("keyDistributePlatform", keyDistributePlatform);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E121 更新非对称密钥
     *
     * @param keyName    密钥名称
     * @param mode       模式
     * @param exportFlag 私钥输出标识
     * @param activeDate 生效日期
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pkValue} 	：公钥值；
     * {@code string = vkValue} 	：私钥值。
     */
    public UnionMessage servE121(String keyName, String mode, String exportFlag, String activeDate) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E121");

        //组装报文体
        um.putBodyField("keyName", keyName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(exportFlag)) {
            um.putBodyField("exportFlag", exportFlag);
        }
        if (!UnionVerify.paramIsEmpty(activeDate)) {
            um.putBodyField("activeDate", activeDate);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E122 导入公钥
     *
     * @param keyName    密钥名称
     * @param format     公钥编码格式
     * @param pkValue    公钥
     * @param activeDate 生效日期
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE122(String keyName, String format, String pkValue, String activeDate) {
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(pkValue)) {
            return UnionVerify.paramValueWrong("pkValue");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E122");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("pkValue", pkValue);

        //判断可选
        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }
        if (!UnionVerify.paramIsEmpty(activeDate)) {
            um.putBodyField("activeDate", activeDate);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E123 导出公钥
     *
     * @param keyName 密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pkValue} 	：公钥
     */
    public UnionMessage servE123(String keyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E123");
        um.putBodyField("keyName", keyName);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E124 申请非对称密钥
     *
     * @param keyName 密钥名称
     * @param mode    模式
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE124(String keyName, String mode) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E124");
        um.putBodyField("keyName", keyName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E125 分发非对称密钥
     *
     * @param keyName 密钥名称
     * @param mode    模式
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE125(String keyName, String mode) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E125");
        um.putBodyField("keyName", keyName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E126 销毁非对称密钥
     *
     * @param keyName 密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE126(String keyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E126");
        um.putBodyField("keyName", keyName);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E127 获取非对称密钥信息
     *
     * @param keyName 密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = keyGroup} 	：密钥组；
     * {@code string = algorithmID} 	：算法标识；
     * {@code string = keyType} 	：密钥类型；
     * {@code string = pkExponent} 	：公钥指数；
     * {@code string = keyLen} 	：密钥长度；
     * {@code string = vkStoreLocation} 	：私钥存储位置；
     * {@code string = vkIndex} 	：私钥索引；
     * {@code string = oldVersionKeyIsUsed} 	：允许使用旧密钥；
     * {@code string = inputFlag} 	：私钥允许导入标识；
     * {@code string = outputFlag} 	：私钥允许导出标识；
     * {@code string = keyUpdateTime} 	：密钥更新时间；
     * {@code string = effectiveDays} 	：有效天数；
     * {@code string = status} 	：状态；
     * {@code string = activeDate} 	：生效日期；
     * {@code string = keyApplyPlatform} 	：密钥申请平台；
     * {@code string = keyDistributePlatform} 	：密钥分发平台；
     * {@code string = creatorType} 	：创建者类型；
     * {@code string = creator} 	：创建者；
     * {@code string = createTime} 	：创建时间；
     * {@code string = usingUnit} 	：组织；
     * {@code string = remark} 	：备注。
     */
    public UnionMessage servE127(String keyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E127");
        um.putBodyField("keyName", keyName);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E130 生成PIN
     *
     * @param pinLen    PIN长度
     * @param accNo     账号
     * @param mode      模式
     * @param pinOffset PINOffset
     * @param keyName   PVK密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock} 	：PIN密文
     */
    public UnionMessage servE130(String pinLen, String accNo, String mode, String pinOffset, String keyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pinLen)) {
            return UnionVerify.paramValueWrong("pinLen");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E130");
        um.putBodyField("pinLen", pinLen);
        um.putBodyField("accNo", accNo);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(pinOffset)) {
            um.putBodyField("pinOffset", pinOffset);
        }
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E131 生成PVV
     *
     * @param keyName      PVK密钥名称
     * @param protectFlag  PIN保护方式
     * @param pinBlock     PIN密文
     * @param keyNameOfZPK ZPK密钥名称（DES），当为（SM4）时，密钥名称填密钥类型为TPK的密钥名称
     * @param accNo        账号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = PVV} 	：PVV
     */
    public UnionMessage servE131(String keyName, String protectFlag, String pinBlock, String keyNameOfZPK, String accNo) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E131");
        um.putBodyField("keyName", keyName);
        um.putBodyField("pinBlock", pinBlock);

        //判断可选
        if (!UnionVerify.paramIsEmpty(protectFlag)) {
            um.putBodyField("protectFlag", protectFlag);
        }
        if (!UnionVerify.paramIsEmpty(keyNameOfZPK)) {
            um.putBodyField("keyNameOfZPK", keyNameOfZPK);
        }
        if (!UnionVerify.paramIsEmpty(accNo)) {
            um.putBodyField("accNo", accNo);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E132 生成PIN Offset
     *
     * @param keyName      PVK密钥名称
     * @param pinBlock     PIN密文
     * @param minPinLen    最小的PIN长度
     * @param protectFlag  PIN保护方式
     * @param keyNameOfZPK ZPK密钥名称
     * @param accNo        账号
     * @param checkFlag    检验标识
     * @param checkData    PIN校验数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = checkData} 	：PIN校验数据
     */
    public UnionMessage servE132(String keyName, String pinBlock, String minPinLen, String protectFlag, String keyNameOfZPK, String accNo, String checkFlag, String checkData) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E132");
        um.putBodyField("keyName", keyName);
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("accNo", accNo);

        //判断可选
        if (!UnionVerify.paramIsEmpty(minPinLen)) {
            um.putBodyField("minPinLen", minPinLen);
        }
        if (!UnionVerify.paramIsEmpty(protectFlag)) {
            um.putBodyField("protectFlag", protectFlag);
        }
        if (!UnionVerify.paramIsEmpty(keyNameOfZPK)) {
            um.putBodyField("keyNameOfZPK", keyNameOfZPK);
        }
        if (!UnionVerify.paramIsEmpty(checkFlag)) {
            um.putBodyField("checkFlag", checkFlag);
        }
        if (!UnionVerify.paramIsEmpty(checkData)) {
            um.putBodyField("checkData", checkData);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E133 生成CVV
     *
     * @param keyName        CVK密钥名称
     * @param expirationDate 有效期
     * @param serviceCode    服务代码
     * @param accNo          账号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = CVV} 	：CVV
     */
    public UnionMessage servE133(String keyName, String expirationDate, String serviceCode, String accNo) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(expirationDate)) {
            return UnionVerify.paramValueWrong("expirationDate");
        }
        if (UnionVerify.paramIsEmpty(serviceCode)) {
            return UnionVerify.paramValueWrong("serviceCode");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E133");
        um.putBodyField("keyName", keyName);
        um.putBodyField("expirationDate", expirationDate);
        um.putBodyField("serviceCode", serviceCode);
        um.putBodyField("accNo", accNo);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E140 加密PIN
     *
     * @param plainPin 明文PIN
     * @param isDigit  是否是数字PIN
     * @param accNo    账号
     * @param mode     模式
     * @param format   格式
     * @param keyName  ZPK密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock} 	：PIN密文
     */
    public UnionMessage servE140(String plainPin, String isDigit, String accNo, String mode, String format, String keyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(plainPin)) {
            return UnionVerify.paramValueWrong("plainPin");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E140");
        um.putBodyField("plainPin", plainPin);
        um.putBodyField("accNo", accNo);

        //判断可选
        if (!UnionVerify.paramIsEmpty(isDigit)) {
            um.putBodyField("isDigit", isDigit);
        }
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E141 解密PIN
     *
     * @param pinBlock PIN密文
     * @param accNo    账号
     * @param mode     模式
     * @param format   格式
     * @param keyName  ZPK密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = plainPin} 	：PIN明文
     * {@code string = referenceNumber} 	：参考数
     */
    public UnionMessage servE141(String pinBlock, String accNo, String mode, String format, String keyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E141");
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("accNo", accNo);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E142 转加密PIN
     *
     * @param pinBlock     PIN密文
     * @param zmkKeyName   源密钥保护密钥
     * @param keyName1     源密钥名称
     * @param keyName2     目的密钥名称
     * @param accNo1       源账号
     * @param accNo2       目的账号
     * @param format1      源PIN格式
     * @param format2      目的PIN格式
     * @param checkFlag    检验标识
     * @param checkData    PIN校验数据
     * @param decimalFlag  十进制转换标识
     * @param decimalTable 十进制转换表
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock} 	：PIN密文
     * {@code string = pinOffset} 	：PIN Offset
     * {@code string = checkData} 	：PIN校验数据
     * {@code string = decimalTable} 	：十进制转换表
     */
    public UnionMessage servE142(String pinBlock, String zmkKeyName, String keyName1, String keyName2,
                                 String accNo1, String accNo2, String format1, String format2, String checkFlag, String checkData,
                                 String decimalFlag, String decimalTable) {

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E142");
        //组装报文体
        um.putBodyField("pinBlock", pinBlock);
        if (zmkKeyName != null) {
            um.putBodyField("zmkKeyName", zmkKeyName);
        }
        if (keyName1 != null) {
            um.putBodyField("keyName1", keyName1);
            um.putBodyField("accNo1", accNo1);
            um.putBodyField("format1", format1);
        }
        if (keyName2 != null) {
            um.putBodyField("keyName2", keyName2);
            um.putBodyField("accNo2", accNo2);
            um.putBodyField("format2", format2);
            um.putBodyField("checkFlag", checkFlag);
            if ("1".equals(checkFlag)) {
                um.putBodyField("checkData", checkData);
            }
            um.putBodyField("decimalFlag", decimalFlag);
            if ("1".equals(decimalFlag)) {
                um.putBodyField("decimalTable", decimalTable);
            }
        }
        /**
         * 发送和接收
         * um 报文对象
         * api.getMaxSendTimes() 获取初始化时候最大连接次数
         */
        um = UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
        return um;
    }

    /**
     * E143 验证PIN
     *
     * @param pinBlock      PIN密文
     * @param format        PIN格式
     * @param accNo         账号
     * @param keyName       ZPK名称
     * @param mode          模式
     * @param keyNameOfPVK  PVK名称
     * @param pinOffset     PIN Offset
     * @param checkFlag     检验标识
     * @param checkData     PIN校验数据
     * @param PVV           PVV
     * @param pinBlockByLMK LMK加密的PIN密文
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE143(String pinBlock, String format, String accNo, String keyName,
                                 String mode, String keyNameOfPVK, String pinOffset, String checkFlag, String checkData, String PVV,
                                 String pinBlockByLMK) {

        //校验参数
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E143");
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("accNo", accNo);
        um.putBodyField("keyName", keyName);
        um.putBodyField("mode", mode);

        //判断可选
        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }
        if (!UnionVerify.paramIsEmpty(keyNameOfPVK)) {
            um.putBodyField("keyNameOfPVK", keyNameOfPVK);
        }
        if (!UnionVerify.paramIsEmpty(pinOffset)) {
            um.putBodyField("pinOffset", pinOffset);
        }
        if (!UnionVerify.paramIsEmpty(checkFlag)) {
            um.putBodyField("checkFlag", checkFlag);
        }
        if (!UnionVerify.paramIsEmpty(checkData)) {
            um.putBodyField("checkData", checkData);
        }
        if (!UnionVerify.paramIsEmpty(PVV)) {
            um.putBodyField("PVV", PVV);
        }
        if (!UnionVerify.paramIsEmpty(pinBlockByLMK)) {
            um.putBodyField("pinBlockByLMK", pinBlockByLMK);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E144 转加密PIN Offset
     *
     * @param pinLen     最小PIN长度
     * @param pinOffset  PIN Offset
     * @param keyName1   源PVK密钥名称
     * @param keyName2   目的PVK密钥名称
     * @param accNo1     源账号
     * @param accNo2     目的账号
     * @param checkFlag1 检验标识
     * @param checkData1 PIN校验数据
     * @param checkFlag2 检验标识
     * @param checkData2 PIN校验数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinOffset} 	：PIN Offset
     * {@code string = checkData} 	：PIN校验数据
     */
    public UnionMessage servE144(String pinLen, String pinOffset, String keyName1, String keyName2,
                                 String accNo1, String accNo2, String checkFlag1, String checkData1, String checkFlag2, String checkData2) {

        //校验参数
        if (UnionVerify.paramIsEmpty(pinLen)) {
            return UnionVerify.paramValueWrong("pinLen");
        }
        if (UnionVerify.paramIsEmpty(pinOffset)) {
            return UnionVerify.paramValueWrong("pinOffset");
        }
        if (UnionVerify.paramIsEmpty(keyName1)) {
            return UnionVerify.paramValueWrong("keyName1");
        }
        if (UnionVerify.paramIsEmpty(keyName2)) {
            return UnionVerify.paramValueWrong("keyName2");
        }
        if (UnionVerify.paramIsEmpty(accNo1)) {
            return UnionVerify.paramValueWrong("accNo1");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E144");
        um.putBodyField("pinLen", pinLen);
        um.putBodyField("pinOffset", pinOffset);
        um.putBodyField("keyName1", keyName1);
        um.putBodyField("keyName2", keyName2);
        um.putBodyField("accNo1", accNo1);

        //判断可选
        if (!UnionVerify.paramIsEmpty(accNo2)) {
            um.putBodyField("accNo2", accNo2);
        }
        if (!UnionVerify.paramIsEmpty(checkFlag1)) {
            um.putBodyField("checkFlag1", checkFlag1);
        }
        if (!UnionVerify.paramIsEmpty(checkData1)) {
            um.putBodyField("checkData1", checkData1);
        }
        if (!UnionVerify.paramIsEmpty(checkFlag2)) {
            um.putBodyField("checkFlag2", checkFlag2);
        }
        if (!UnionVerify.paramIsEmpty(checkData2)) {
            um.putBodyField("checkData2", checkData2);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E145 验证CVV
     *
     * @param CVV            待验证的CVV
     * @param expirationDate 有效期
     * @param serviceCode    服务代码
     * @param accNo          账号
     * @param keyName        CVK密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE145(String CVV, String expirationDate, String serviceCode, String accNo, String keyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(CVV)) {
            return UnionVerify.paramValueWrong("CVV");
        }
        if (UnionVerify.paramIsEmpty(expirationDate)) {
            return UnionVerify.paramValueWrong("expirationDate");
        }
        if (UnionVerify.paramIsEmpty(serviceCode)) {
            return UnionVerify.paramValueWrong("serviceCode");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E145");
        um.putBodyField("CVV", CVV);
        um.putBodyField("expirationDate", expirationDate);
        um.putBodyField("serviceCode", serviceCode);
        um.putBodyField("accNo", accNo);
        um.putBodyField("keyName", keyName);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E146 将ZPK（DES和SM4）加密的pin转成SM2公钥加密
     *
     * @param keyName    ZPK密钥名称
     * @param format     PIN格式
     * @param accNo      账号
     * @param pinBlock   ZPK加密的PIN
     * @param SM2KeyName 公钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinByPK} 	：公钥加密的PIN密文
     */
    public UnionMessage servE146(String keyName, String format, String accNo, String pinBlock, String SM2KeyName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(format)) {
            return UnionVerify.paramValueWrong("format");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(SM2KeyName)) {
            return UnionVerify.paramValueWrong("SM2KeyName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E146");
        um.putBodyField("keyName", keyName);
        um.putBodyField("format", format);
        um.putBodyField("accNo", accNo);
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("SM2KeyName", SM2KeyName);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E148 SM4和DES pinBlock转成MD5和SM3，公钥加密的渠道密码转换成SM3/MD5
     *
     * @param mode         模式
     * @param keyName      密钥名称
     * @param pinBlock     PIN块或者公钥加密的密文
     * @param accNo        账号
     * @param algorithmID  算法标识
     * @param dataFillMode 数据补位方式
     * @param saltedOffset 加盐偏移
     * @param saltedData   加盐数据
     * @param dataFillHead 前填充数据
     * @param dataFillTail 后填充数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = hash} 	：HASH
     */
    public UnionMessage servE148(String mode, String keyName, String pinBlock, String accNo,
                                 String algorithmID, String dataFillMode, String saltedOffset, String saltedData, String dataFillHead, String dataFillTail) {

        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(algorithmID)) {
            return UnionVerify.paramValueWrong("algorithmID");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E148");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("algorithmID", algorithmID);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(accNo)) {
            um.putBodyField("accNo", accNo);
        }
        if (!UnionVerify.paramIsEmpty(dataFillMode)) {
            um.putBodyField("dataFillMode", dataFillMode);
        }
        if (!UnionVerify.paramIsEmpty(saltedOffset)) {
            um.putBodyField("saltedOffset", saltedOffset);
        }
        if (!UnionVerify.paramIsEmpty(saltedData)) {
            um.putBodyField("saltedData", saltedData);
        }
        if (!UnionVerify.paramIsEmpty(dataFillHead)) {
            um.putBodyField("dataFillHead", dataFillHead);
        }
        if (!UnionVerify.paramIsEmpty(dataFillTail)) {
            um.putBodyField("dataFillTail", dataFillTail);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E149 弱密码检查(只支持交易密码)
     *
     * @param keyName      密钥名称
     * @param format       pin块格式
     * @param accNo        账号
     * @param phoneNumber  手机号码
     * @param identityCard 身份证号
     * @param verifyData1  验证数据1
     * @param verifyData2  验证数据2
     * @param pinBlock     pin密文
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = resultFlag} 	：检查结果标识
     */
    public UnionMessage servE149(String keyName, String format, String accNo, String phoneNumber, String identityCard,
                                 String verifyData1, String verifyData2, String pinBlock) {

        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(format)) {
            return UnionVerify.paramValueWrong("format");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(phoneNumber)) {
            return UnionVerify.paramValueWrong("phoneNumber");
        }
        if (UnionVerify.paramIsEmpty(identityCard)) {
            return UnionVerify.paramValueWrong("identityCard");
        }
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E149");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("format", format);
        um.putBodyField("accNo", accNo);
        um.putBodyField("phoneNumber", phoneNumber);
        um.putBodyField("identityCard", identityCard);
        um.putBodyField("pinBlock", pinBlock);

        //判断可选
        if (!UnionVerify.paramIsEmpty(verifyData1)) {
            um.putBodyField("verifyData1", verifyData1);
        }
        if (!UnionVerify.paramIsEmpty(verifyData2)) {
            um.putBodyField("verifyData2", verifyData2);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E150 生成MAC		//2017.06.09 linxl 根据文档添加了参数keyVersion
     *
     * @param keyVersion  密钥版本
     * @param mode        模式
     * @param keyName     密钥名称
     * @param keyValue    密钥密文
     * @param algorithmID 算法标识
     * @param fillMode    自动填充方式
     * @param dataType    数据类型
     * @param data        数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：MAC
     */
    public UnionMessage servE150(String keyVersion, String mode, String keyName, String keyValue, String algorithmID,
                                 String fillMode, String dataType, String data) {

        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E150");

        //组装报文体
        um.putBodyField("data", data);

        //判断可选
        if (!UnionVerify.paramIsEmpty(keyVersion)) {
            um.putBodyField("keyVersion", keyVersion);
        }
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(keyValue)) {
            um.putBodyField("keyValue", keyValue);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(fillMode)) {
            um.putBodyField("fillMode", fillMode);
        }
        if (!UnionVerify.paramIsEmpty(dataType)) {
            um.putBodyField("dataType", dataType);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E151 验证MAC
     *
     * @param keyVersion  密钥版本
     * @param mode        模式
     * @param keyName     密钥名称
     * @param keyValue    密钥密文
     * @param algorithmID 算法标识
     * @param fillMode    自动填充方式
     * @param dataType    数据类型
     * @param data        数据
     * @param mac         MAC
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE151(String keyVersion, String mode, String keyName, String keyValue, String algorithmID,
                                 String fillMode, String dataType, String data, String mac) {

        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(mac)) {
            return UnionVerify.paramValueWrong("mac");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E151");

        //组装报文体
        um.putBodyField("data", data);
        um.putBodyField("mac", mac);

        //判断可选
        if (!UnionVerify.paramIsEmpty(keyVersion)) {
            um.putBodyField("keyVersion", keyVersion);
        }
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(keyValue)) {
            um.putBodyField("keyValue", keyValue);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(fillMode)) {
            um.putBodyField("fillMode", fillMode);
        }
        if (!UnionVerify.paramIsEmpty(dataType)) {
            um.putBodyField("dataType", dataType);
        }
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E152 验证并生成MAC
     *
     * @param mode        模式
     * @param keyName1    密钥名称1，用于验证mac
     * @param keyValue1   密钥密文1
     * @param keyName2    密钥名称2，用于生成mac
     * @param keyValue2   密钥密文2
     * @param algorithmID 算法标识
     * @param fillMode    自动填充方式
     * @param dataType    数据类型
     * @param data        数据
     * @param mac         待验证MAC
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：新生成MAC
     */
    public UnionMessage servE152(String mode, String keyName1, String keyValue1, String keyName2, String keyValue2,
                                 String algorithmID, String fillMode, String dataType, String data, String mac) {

        if (UnionVerify.paramIsEmpty(keyName1)) {
            return UnionVerify.paramValueWrong("keyName1");
        }
        if (UnionVerify.paramIsEmpty(keyName2)) {
            return UnionVerify.paramValueWrong("keyName2");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(mac)) {
            return UnionVerify.paramValueWrong("mac");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E152");

        //组装报文体
        um.putBodyField("keyName1", keyName1);
        um.putBodyField("keyName2", keyName2);
        um.putBodyField("data", data);
        um.putBodyField("mac", mac);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(keyValue1)) {
            um.putBodyField("keyValue1", keyValue1);
        }
        if (!UnionVerify.paramIsEmpty(keyValue2)) {
            um.putBodyField("keyValue2", keyValue2);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(fillMode)) {
            um.putBodyField("fillMode", fillMode);
        }
        if (!UnionVerify.paramIsEmpty(dataType)) {
            um.putBodyField("dataType", dataType);
        }
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E160 加密数据
     *
     * @param mode        模式
     * @param keyName     密钥名称
     * @param keyValue    密钥密文
     * @param algorithmID 算法标识
     * @param data        明文数据
     * @param iv          初始向量
     * @param format      密文数据格式
     * @param dataType    数据类型
     * @param separator   分隔符
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = Data} 	：密文数据
     */
    public UnionMessage servE160(String mode, String keyName, String keyValue, String algorithmID, String data,
                                 String iv, String format, String dataType, String separator) {

        //判断必填项
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        UnionMessage um = new UnionRequest();

        //组装服务码
        um.putHeadField("serviceCode", "E160");

        //组装报文体
        um.putBodyField("data", data);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        } else {
            um.putBodyField("mode", "1");
        }

        if (!UnionVerify.paramIsEmpty(keyName) && (!"3".equals(mode) || !"4".equals(mode))) {
            um.putBodyField("keyName", keyName);
        } else {
            return UnionVerify.paramValueWrong("keyName");
        }

        if (!UnionVerify.paramIsEmpty(keyValue) && !"1".equals(mode)) {
            um.putBodyField("keyValue", keyValue);
        } else {
            um.putBodyField("keyValue", keyValue);
        }

        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }

        if (!UnionVerify.paramIsEmpty(iv) && "1".equals(algorithmID)) {
            um.putBodyField("iv", iv);
        } else {
            return UnionVerify.paramValueWrong("iv");
        }

        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }
        if (!UnionVerify.paramIsEmpty(dataType)) {
            um.putBodyField("dataType", dataType);
        }
        if (!UnionVerify.paramIsEmpty(separator)) {
            um.putBodyField("separator", separator);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E161解密数据
     *
     * @param mode        模式
     * @param keyName     密钥名称
     * @param keyValue    密钥密文
     * @param algorithmID 算法标识
     * @param exportFlag  输出标识
     * @param data        密文数据
     * @param iv          初始向量
     * @param format      密文数据格式
     * @param separator   分隔符
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = data} 	：明文数据
     */
    public UnionMessage servE161(String mode, String keyName, String keyValue, String algorithmID, String exportFlag, String data,
                                 String iv, String format, String separator) {

        //判断必填项
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        UnionMessage um = new UnionRequest();

        //组装服务码
        um.putHeadField("serviceCode", "E161");

        //组装报文体
        um.putBodyField("data", data);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        } else {
            mode = "1";
            um.putBodyField("mode", mode);
        }

        if (!UnionVerify.paramIsEmpty(keyName) && (!"3".equals(mode) || !"4".equals(mode))) {
            um.putBodyField("keyName", keyName);
        } else {
            return UnionVerify.paramValueWrong("keyName");
        }

        if (!UnionVerify.paramIsEmpty(keyValue) && !"1".equals(mode)) {
            um.putBodyField("keyValue", keyValue);
        } else {
            return UnionVerify.paramValueWrong("keyValue");
        }

        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        } else {
            algorithmID = "0";
            um.putBodyField("algorithmID", algorithmID);
        }

        if (!UnionVerify.paramIsEmpty(exportFlag)) {
            um.putBodyField("exportFlag", exportFlag);
        }

        if (!UnionVerify.paramIsEmpty(iv) && "1".equals(algorithmID)) {
            um.putBodyField("iv", iv);
        } else {
            return UnionVerify.paramValueWrong("iv");
        }

        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }
        if (!UnionVerify.paramIsEmpty(separator)) {
            um.putBodyField("separator", separator);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E163 从ZEK加密转成另一把ZEK加密
     *
     * @param zekKeyName1 ZEK1密钥名称
     * @param cipherData  密文数据
     * @param algorithmID 算法标志
     * @param IV          初始向量
     * @param dst_IV      目的初始向量
     * @param format      密文数据格式
     * @param zekKeyName2 ZEK2密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = cipherData} 	：目的zek密钥加密的数据
     */
    public UnionMessage servE163(String zekKeyName1, String cipherData, String algorithmID, String IV, String dst_IV,
                                 String format, String zekKeyName2) {

        //判断必填项
        if (UnionVerify.paramIsEmpty(zekKeyName1)) {
            return UnionVerify.paramValueWrong("zekKeyName1");
        }
        if (UnionVerify.paramIsEmpty(cipherData)) {
            return UnionVerify.paramValueWrong("cipherData");
        }
        if (UnionVerify.paramIsEmpty(zekKeyName2)) {
            return UnionVerify.paramValueWrong("zekKeyName2");
        }

        UnionMessage um = new UnionRequest();

        //组装服务码
        um.putHeadField("serviceCode", "E163");

        //组装报文体
        um.putBodyField("zekKeyName1", zekKeyName1);
        um.putBodyField("cipherData", cipherData);
        um.putBodyField("zekKeyName2", zekKeyName2);

        //判断可选项，存在则组装进报文体
        if (UnionVerify.paramIsEmpty(algorithmID)) {
            algorithmID = "0";
        }
        um.putBodyField("algorithmID", algorithmID);

        if (!UnionVerify.paramIsEmpty(IV) && "1".equals(algorithmID)) {
            um.putBodyField("IV", IV);
        } else {
            return UnionVerify.paramValueWrong("IV");
        }

        if (!UnionVerify.paramIsEmpty(dst_IV)) {
            um.putBodyField("dst_IV", dst_IV);
        }
        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E165 数字信封数据加密
     *
     * @param keyName
     * @param algorithmID
     * @param iv
     * @param fillMode
     * @param dataType
     * @param data
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = encryptData} 	：密文数据/ LMK加密的对称密钥+密钥校验值；
     * {@code string = keyByPK} ：公钥加密的ZEk密钥。
     */
    public UnionMessage servE165(String keyName, String algorithmID, String iv, String fillMode,
                                 String dataType, String data) {

        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }

        UnionMessage um = new UnionRequest();

        //组装服务码
        um.putHeadField("serviceCode", "E165");

        //组装报文体
        um.putBodyField("keyName", keyName);

        //判断可选项，存在则组装进报文体
        if (UnionVerify.paramIsEmpty(algorithmID)) {
            algorithmID = "0";
        }
        um.putBodyField("algorithmID", algorithmID);

        if (!UnionVerify.paramIsEmpty(iv)) {
            um.putBodyField("iv", iv);
        }
        if (!UnionVerify.paramIsEmpty(fillMode)) {
            um.putBodyField("fillMode", fillMode);
        }
        if (!UnionVerify.paramIsEmpty(dataType)) {
            um.putBodyField("dataType", dataType);
        }
        if (!UnionVerify.paramIsEmpty(data)) {
            um.putBodyField("data", data);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E166 数字信封数据解密
     *
     * @param keyName     非对称密钥名称
     * @param keyByPK     公钥加密的ZEk密钥
     * @param algorithmID 数据解密算法标识
     * @param iv          初始向量
     * @param fillMode    数据填充方式
     * @param dataType    数据类型
     * @param exportFlag  输出标识
     * @param encryptData 密文数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = data} 	：明文数据
     */
    public UnionMessage servE166(String keyName, String keyByPK, String algorithmID, String iv, String fillMode,
                                 String dataType, String exportFlag, String encryptData) {

        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(keyByPK)) {
            return UnionVerify.paramValueWrong("keyByPK");
        }

        UnionMessage um = new UnionRequest();

        //组装服务码
        um.putHeadField("serviceCode", "E166");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("keyByPK", keyByPK);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(iv)) {
            um.putBodyField("iv", iv);
        }
        if (!UnionVerify.paramIsEmpty(fillMode)) {
            um.putBodyField("fillMode", fillMode);
        }
        if (!UnionVerify.paramIsEmpty(dataType)) {
            um.putBodyField("dataType", dataType);
        }
        if (!UnionVerify.paramIsEmpty(exportFlag)) {
            um.putBodyField("exportFlag", exportFlag);
        }
        if (!UnionVerify.paramIsEmpty(encryptData)) {
            um.putBodyField("encryptData", encryptData);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E170 私钥签名
     *
     * @param keyName      密钥名称
     * @param dataFillMode 数据补位方式
     * @param hashID       HASH算法标识
     * @param userID       用户标识
     * @param data         签名数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = sign} 	：签名
     */
    public UnionMessage servE170(String keyName, String dataFillMode, String hashID, String userID, String data) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        UnionMessage um = new UnionRequest();

        //组装服务码
        um.putHeadField("serviceCode", "E170");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("data", data);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(dataFillMode)) {
            um.putBodyField("dataFillMode", dataFillMode);
        }
        if (!UnionVerify.paramIsEmpty(hashID)) {
            um.putBodyField("hashID", hashID);
        }
        if (!UnionVerify.paramIsEmpty(userID)) {
            um.putBodyField("userID", userID);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E171 公钥验签
     *
     * @param mode         模式
     * @param keyName      公开密钥名称
     * @param pkValue      公钥
     * @param algorithmID  算法标识
     * @param pkExponent   公钥指数
     * @param dataFillMode 数据补位方式
     * @param hashID       HASH算法标识
     * @param userID       用户标识
     * @param data         签名数据
     * @param sign         签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE171(String mode, String keyName, String pkValue, String algorithmID, String pkExponent,
                                 String dataFillMode, String hashID, String userID, String data, String sign) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(sign)) {
            return UnionVerify.paramValueWrong("sign");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E171");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("data", data);
        um.putBodyField("sign", sign);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(pkValue)) {
            um.putBodyField("pkValue", pkValue);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(pkExponent)) {
            um.putBodyField("pkExponent", pkExponent);
        }
        if (!UnionVerify.paramIsEmpty(dataFillMode)) {
            um.putBodyField("dataFillMode", dataFillMode);
        }
        if (!UnionVerify.paramIsEmpty(hashID)) {
            um.putBodyField("hashID", hashID);
        }
        if (!UnionVerify.paramIsEmpty(userID)) {
            um.putBodyField("userID", userID);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E172 公钥加密
     *
     * @param mode         模式
     * @param keyName      公开密钥名称
     * @param pkValue      公钥
     * @param algorithmID  算法标识
     * @param pkExponent   公钥指数
     * @param dataFillMode 数据补位方式
     * @param plainData    明文数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = cipherData} 	：密文数据
     */
    public UnionMessage servE172(String mode, String keyName, String pkValue, String algorithmID, String pkExponent,
                                 String dataFillMode, String plainData) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(plainData)) {
            return UnionVerify.paramValueWrong("plainData");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E172");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("plainData", plainData);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(pkValue)) {
            um.putBodyField("pkValue", pkValue);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(pkExponent)) {
            um.putBodyField("pkExponent", pkExponent);
        }
        if (!UnionVerify.paramIsEmpty(dataFillMode)) {
            um.putBodyField("dataFillMode", dataFillMode);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E173 私钥解密
     *
     * @param keyName       密钥名称
     * @param cipherDataLen 密文数据长度
     * @param algorithmID   算法标识
     * @param vkIndex       私钥索引
     * @param dataFillMode  公钥加密时采用的填充方式
     * @param cipherData    密文数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = plainData} 	：明文数据
     */
    public UnionMessage servE173(String keyName, String cipherDataLen, String algorithmID, String vkIndex,
                                 String dataFillMode, String cipherData) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(cipherDataLen)) {
            return UnionVerify.paramValueWrong("cipherDataLen");
        }
        if (UnionVerify.paramIsEmpty(cipherData)) {
            return UnionVerify.paramValueWrong("cipherData");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E173");

        //组装报文体
        um.putBodyField("cipherDataLen", cipherDataLen);
        um.putBodyField("cipherData", cipherData);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(vkIndex)) {
            um.putBodyField("vkIndex", vkIndex);
        }
        if (!UnionVerify.paramIsEmpty(dataFillMode)) {
            um.putBodyField("dataFillMode", dataFillMode);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E180 计算数据摘要
     *
     * @param data     摘要数据
     * @param dataType 数据类型
     * @param hashID   Hash算法标识
     * @param mode     SM3摘要模式
     * @param pk       公钥名称或密钥值
     * @param userID   用户ID
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = digest} 	：摘要
     */
    public UnionMessage servE180(String data, String dataType, String hashID, String mode, String pk, String userID) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(hashID)) {
            return UnionVerify.paramValueWrong("hashID");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "E180");

        //组装报文体
        um.putBodyField("data", data);
        um.putBodyField("hashID", hashID);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(dataType)) {
            um.putBodyField("dataType", dataType);
        }
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(pk)) {
            um.putBodyField("pk", pk);
        }
        if (!UnionVerify.paramIsEmpty(userID)) {
            um.putBodyField("userID", userID);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E201 数字PIN转换(PK->ZPK)，支持RSA转DES   SM2转DES   SM2转SM4
     *
     * @param pkKeyName  公钥名称
     * @param zpkKeyName ZPK密钥名称
     * @param accNo      账号
     * @param pinByPK    公钥加密的PIN
     * @param format     PIN格式
     * @param specialAlg 专用算法标示
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinByZPK} 	：ZPK加密的PIN
     */
    public UnionMessage servE201(String pkKeyName, String zpkKeyName, String accNo, String pinByPK,
                                 String format, String specialAlg) {

        if (UnionVerify.paramIsEmpty(pkKeyName)) {
            return UnionVerify.paramValueWrong("pkKeyName");
        }
        if (UnionVerify.paramIsEmpty(zpkKeyName)) {
            return UnionVerify.paramValueWrong("zpkKeyName");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(pinByPK)) {
            return UnionVerify.paramValueWrong("pinByPK");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E201");

        //组装报文体
        um.putBodyField("pkKeyName", pkKeyName);
        um.putBodyField("zpkKeyName", zpkKeyName);
        um.putBodyField("accNo", accNo);
        um.putBodyField("pinByPK", pinByPK);

        //判断可选
        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }
        if (!UnionVerify.paramIsEmpty(specialAlg)) {
            um.putBodyField("specialAlg", specialAlg);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E202 字符PIN转换(PK->ZPK)
     *
     * @param pkKeyName  公钥名称
     * @param zpkKeyName ZPK密钥名称
     * @param accNo      账号
     * @param pinByPK    公钥加密的PIN
     * @param format     PIN格式
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinByZPK} 	：ZPK加密的PIN
     */
    public UnionMessage servE202(String pkKeyName, String zpkKeyName, String accNo, String pinByPK, String format) {

        if (UnionVerify.paramIsEmpty(pkKeyName)) {
            return UnionVerify.paramValueWrong("pkKeyName");
        }
        if (UnionVerify.paramIsEmpty(zpkKeyName)) {
            return UnionVerify.paramValueWrong("zpkKeyName");
        }
        if (UnionVerify.paramIsEmpty(pinByPK)) {
            return UnionVerify.paramValueWrong("pinByPK");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E202");

        //组装报文体
        um.putBodyField("pkKeyName", pkKeyName);
        um.putBodyField("zpkKeyName", zpkKeyName);
        um.putBodyField("pinByPK", pinByPK);

        //判断可选
        if (!UnionVerify.paramIsEmpty(accNo)) {
            um.putBodyField("accNo", accNo);
        }
        if (!UnionVerify.paramIsEmpty(format)) {
            um.putBodyField("format", format);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * E203 字符PIN转换(ZPK->ZPK)
     *
     * @param pinBlock PIN密文
     * @param format1  源PIN格式
     * @param format2  目的PIN格式
     * @param accNo1   源账号
     * @param accNo2   目的账号
     * @param keyName1 源密钥名称
     * @param keyName2 目的密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock} 	：PIN密文
     */
    public UnionMessage servE203(String pinBlock, String format1, String format2, String accNo1, String accNo2,
                                 String keyName1, String keyName2) {

        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(accNo1)) {
            return UnionVerify.paramValueWrong("accNo1");
        }
        if (UnionVerify.paramIsEmpty(accNo2)) {
            return UnionVerify.paramValueWrong("accNo2");
        }
        if (UnionVerify.paramIsEmpty(keyName1)) {
            return UnionVerify.paramValueWrong("keyName1");
        }
        if (UnionVerify.paramIsEmpty(keyName2)) {
            return UnionVerify.paramValueWrong("keyName2");
        }

        //组装服务代码
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E203");

        //组装报文体
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("accNo1", accNo1);
        um.putBodyField("accNo2", accNo2);
        um.putBodyField("keyName1", keyName1);
        um.putBodyField("keyName2", keyName2);

        //判断可选
        if (!UnionVerify.paramIsEmpty(format1)) {
            um.putBodyField("format1", format1);
        }
        if (!UnionVerify.paramIsEmpty(format2)) {
            um.putBodyField("format2", format2);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E204 字符PIN转换，带ID码返回(PK->ZPK)
     * 只支持国际算法
     * 使用N6指令
     *
     * @param pkKeyName  公钥名称
     * @param zpkKeyName ZPK密钥名称
     * @param accNo      账号
     * @param pinByPK    公钥加密的PIN
     * @param fillMode   用公钥加密PIN时所采用的PIN填充方式
     *                   “0”：如果数据长度小于密钥长度，加密时先在数据前面补0x00，以使数据长度等于密钥的长度，然后再进行加密；
     *                   “1”：PKCS填充方式（一般情况下使用此方式）
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinLen} 	：密码长度；
     * {@code string = pinByZPK} 	：ZPK加密的PIN；
     * {@code string = pinByZPK2} ；
     * {@code string = uniqueID} 	：ID码明文；
     */
    public UnionMessage servE204(String pkKeyName, String zpkKeyName, String accNo, String pinByPK, String fillMode) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pkKeyName)) {
            return UnionVerify.paramValueWrong("pkKeyName");
        }
        if (UnionVerify.paramIsEmpty(zpkKeyName)) {
            return UnionVerify.paramValueWrong("zpkKeyName");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(pinByPK)) {
            return UnionVerify.paramValueWrong("pinByPK");
        }
        if (UnionVerify.paramIsEmpty(fillMode)) {
            return UnionVerify.paramValueWrong("fillMode");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E204");
        um.putBodyField("pkKeyName", pkKeyName);
        um.putBodyField("zpkKeyName", zpkKeyName);
        um.putBodyField("accNo", accNo);
        um.putBodyField("pinByPK", pinByPK);
        um.putBodyField("fillMode", fillMode);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E205 将PIN由X9.8转为特殊算法加密(zpk->zpk)
     *
     * @param pinBlock PIN密文
     * @param accNo1   源账号
     * @param accNo2   目的账号
     * @param keyName1 源密钥名称
     * @param keyName2 目的密钥名称
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock} 	：PIN密文
     */
    public UnionMessage servE205(String pinBlock, String accNo1, String accNo2, String keyName1, String keyName2) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(accNo1)) {
            return UnionVerify.paramValueWrong("accNo1");
        }
        if (UnionVerify.paramIsEmpty(accNo2)) {
            return UnionVerify.paramValueWrong("accNo2");
        }
        if (UnionVerify.paramIsEmpty(keyName1)) {
            return UnionVerify.paramValueWrong("keyName1");
        }
        if (UnionVerify.paramIsEmpty(keyName2)) {
            return UnionVerify.paramValueWrong("keyName2");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E205");
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("accNo1", accNo1);
        um.putBodyField("accNo2", accNo2);
        um.putBodyField("keyName1", keyName1);
        um.putBodyField("keyName2", keyName2);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E207 SM2公钥加密的PIN用SM2私钥解密后由SM4算法ZPK加密
     *
     * @param mode         模式
     * @param keyName      密钥名称
     * @param vkIndex      私钥索引
     * @param vkValue      私钥值
     * @param dataFillMode 公钥加密填充模式
     * @param pinFormat    Pin块格式
     * @param pinByPK      公钥加密的pin
     * @param zpkName      ZPK密钥名称
     * @param accNo        账号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinByZPK} 	：zpk加密的pin
     */
    public UnionMessage servE207(String mode, String keyName, String vkIndex, String vkValue, String dataFillMode,
                                 String pinFormat, String pinByPK, String zpkName, String accNo) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pinByPK)) {
            return UnionVerify.paramValueWrong("pinByPK");
        }
        if (UnionVerify.paramIsEmpty(zpkName)) {
            return UnionVerify.paramValueWrong("zpkName");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E207");

        um.putBodyField("pinByPK", pinByPK);
        um.putBodyField("zpkName", zpkName);
        um.putBodyField("accNo", accNo);

        //判断可选
        if (!UnionVerify.paramIsEmpty(mode)) {
            um.putBodyField("mode", mode);
        }
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(vkIndex)) {
            um.putBodyField("vkIndex", vkIndex);
        }
        if (!UnionVerify.paramIsEmpty(vkValue)) {
            um.putBodyField("vkValue", vkValue);
        }
        if (!UnionVerify.paramIsEmpty(dataFillMode)) {
            um.putBodyField("dataFillMode", dataFillMode);
        }
        if (!UnionVerify.paramIsEmpty(pinFormat)) {
            um.putBodyField("pinFormat", pinFormat);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E212 将SM2公钥加密的SM4密钥转化为LMK加密，解密SM4加密的数据
     *
     * @param keyName    密钥名称
     * @param cipherData 公钥加密密钥的密文
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = data} 	：数据明文
     */
    public UnionMessage servE212(String keyName, String cipherData) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(cipherData)) {
            return UnionVerify.paramValueWrong("cipherData");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E212");

        um.putBodyField("keyName", keyName);
        um.putBodyField("cipherData", cipherData);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E301 校验ARQC(525)
     *
     * @param keyName  mk-ac密钥名称
     * @param version  密钥的版本号
     * @param pan      卡号或（卡号+卡序列号）
     * @param atc      离散过程因子
     * @param arqcData 计算ARQC使用的数据
     * @param arqc     待验证的TC/ARQC/ACC
     * @param iccType  Ic卡类型
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE301(String keyName, String version, String pan, String atc,
                                 String arqcData, String arqc, String iccType) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(version)) {
            return UnionVerify.paramValueWrong("version");
        }
        if (UnionVerify.paramIsEmpty(pan)) {
            return UnionVerify.paramValueWrong("pan");
        }
        if (UnionVerify.paramIsEmpty(atc)) {
            return UnionVerify.paramValueWrong("atc");
        }
        if (UnionVerify.paramIsEmpty(arqcData)) {
            return UnionVerify.paramValueWrong("arqcData");
        }
        if (UnionVerify.paramIsEmpty(arqc)) {
            return UnionVerify.paramValueWrong("arqc");
        }
        if (UnionVerify.paramIsEmpty(iccType)) {
            return UnionVerify.paramValueWrong("iccType");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E301");

        um.putBodyField("keyName", keyName);
        um.putBodyField("version", version);
        um.putBodyField("keyName", keyName);
        um.putBodyField("pan", pan);
        um.putBodyField("atc", atc);
        um.putBodyField("arqcData", arqcData);
        um.putBodyField("arqc", arqc);
        um.putBodyField("iccType", iccType);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E302 生成ARPC(526/529)
     *
     * @param keyName    mk-ac密钥名称
     * @param version    密钥的版本号
     * @param pan        卡号或（卡号+卡序列号）
     * @param atc        离散过程因子
     * @param verifyArqc 是验证ARQC标识
     * @param arqcData   计算ARQC使用的数据
     * @param arqc       待验证的ARQC
     * @param iccType    Ic卡类型
     * @param arc        授权响应码
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = arpc} 	：ARPCs
     */
    public UnionMessage servE302(String keyName, String version, String pan, String atc, String verifyArqc,
                                 String arqcData, String arqc, String iccType, String arc) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(version)) {
            return UnionVerify.paramValueWrong("version");
        }
        if (UnionVerify.paramIsEmpty(pan)) {
            return UnionVerify.paramValueWrong("pan");
        }
        if (UnionVerify.paramIsEmpty(atc)) {
            return UnionVerify.paramValueWrong("atc");
        }
        if (UnionVerify.paramIsEmpty(verifyArqc)) {
            return UnionVerify.paramValueWrong("verifyArqc");
        }
        if (UnionVerify.paramIsEmpty(arqcData)) {
            return UnionVerify.paramValueWrong("arqcData");
        }
        if (UnionVerify.paramIsEmpty(arqc)) {
            return UnionVerify.paramValueWrong("arqc");
        }
        if (UnionVerify.paramIsEmpty(iccType)) {
            return UnionVerify.paramValueWrong("iccType");
        }
        if (UnionVerify.paramIsEmpty(arc)) {
            return UnionVerify.paramValueWrong("arc");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E302");

        um.putBodyField("keyName", keyName);
        um.putBodyField("version", version);
        um.putBodyField("pan", pan);
        um.putBodyField("atc", atc);
        um.putBodyField("verifyArqc", verifyArqc);
        um.putBodyField("arqcData", arqcData);
        um.putBodyField("arqc", arqc);
        um.putBodyField("iccType", iccType);
        um.putBodyField("arc", arc);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E303 加密数据(527)
     *
     * @param keyName   mk-smc密钥名称
     * @param version   密钥的版本号
     * @param pan       卡号+卡序列号
     * @param atc       离散过程因子
     * @param mode      加密模式
     * @param iv        初始向量
     * @param plainData 明文数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = ciperData } 	：密文数据
     */
    public UnionMessage servE303(String keyName, String version, String pan, String atc, String mode,
                                 String iv, String plainData) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(version)) {
            return UnionVerify.paramValueWrong("version");
        }
        if (UnionVerify.paramIsEmpty(pan)) {
            return UnionVerify.paramValueWrong("pan");
        }
        if (UnionVerify.paramIsEmpty(atc)) {
            return UnionVerify.paramValueWrong("atc");
        }
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(plainData)) {
            return UnionVerify.paramValueWrong("plainData");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E303");

        um.putBodyField("keyName", keyName);
        um.putBodyField("version", version);
        um.putBodyField("pan", pan);
        um.putBodyField("atc", atc);
        um.putBodyField("mode", mode);
        um.putBodyField("plainData", plainData);

        //判断可选
        if (!UnionVerify.paramIsEmpty(iv)) {
            um.putBodyField("iv", iv);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E304 脚本数据计算MAC(528)
     *
     * @param keyName   mk-ac密钥名称
     * @param version   密钥的版本号
     * @param pan       卡号+序号
     * @param atc       离散过程因子
     * @param plainData 明文数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：mac值
     */
    public UnionMessage servE304(String keyName, String version, String pan, String atc, String plainData) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(version)) {
            return UnionVerify.paramValueWrong("version");
        }
        if (UnionVerify.paramIsEmpty(pan)) {
            return UnionVerify.paramValueWrong("pan");
        }
        if (UnionVerify.paramIsEmpty(atc)) {
            return UnionVerify.paramValueWrong("atc");
        }
        if (UnionVerify.paramIsEmpty(plainData)) {
            return UnionVerify.paramValueWrong("plainData");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E304");

        um.putBodyField("keyName", keyName);
        um.putBodyField("version", version);
        um.putBodyField("pan", pan);
        um.putBodyField("atc", atc);
        um.putBodyField("plainData", plainData);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E401 生成本行报文
     *
     * @param pix         应用ID
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param algFlag     签名算法
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = sign} 	：数字签名
     */
    public UnionMessage servE401(String pix, String bankID, String certVersion, String data, String algFlag) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E401");

        um.putBodyField("pix", pix);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);
        um.putBodyField("data", data);

        //判断可选
        if (!UnionVerify.paramIsEmpty(algFlag)) {
            um.putBodyField("algFlag", algFlag);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E402 解析他行绑定报文
     *
     * @param pix         应用ID
     * @param mode        模式
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param sign        数字签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = certDN} 	：证书拥有者信息;
     * {@code string = certSN} 	：证书序列号;
     * {@code string = certEffdate} 	：证书起始时间;
     * {@code string = certInvdate} 	：证书结束时间;
     * {@code string = certBuf} 	：公钥证书.
     */
    public UnionMessage servE402(String pix, String mode, String bankID, String certVersion, String data, String sign) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E402");

        um.putBodyField("pix", pix);
        um.putBodyField("mode", mode);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);

        //判断可选
        if (!UnionVerify.paramIsEmpty(data)) {
            um.putBodyField("data", data);
        }
        if (!UnionVerify.paramIsEmpty(sign)) {
            um.putBodyField("sign", sign);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E403 数字签名加签
     *
     * @param pix         应用ID
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param algFlag     签名算法
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = sign} 	：数字签名
     */
    public UnionMessage servE403(String pix, String bankID, String certVersion, String data, String algFlag) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E403");

        um.putBodyField("pix", pix);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);
        um.putBodyField("data", data);

        //判断可选
        if (!UnionVerify.paramIsEmpty(algFlag)) {
            um.putBodyField("algFlag", algFlag);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E404 数字签名核签
     *
     * @param pix         应用ID
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param sign        数字签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE404(String pix, String bankID, String certVersion, String data, String sign) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(sign)) {
            return UnionVerify.paramValueWrong("sign");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E404");

        um.putBodyField("pix", pix);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);
        um.putBodyField("data", data);
        um.putBodyField("sign", sign);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E405 解析他行绑定报文(不验证签名数据)
     *
     * @param pix         应用ID
     * @param mode        模式
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param sign        数字签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE405(String pix, String mode, String bankID, String certVersion, String sign) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E405");

        um.putBodyField("pix", pix);
        um.putBodyField("mode", mode);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);

        //判断可选
        if (!UnionVerify.paramIsEmpty(sign)) {
            um.putBodyField("sign", sign);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E40A 获取证书算法类型及公钥
     *
     * @param pix         应用ID
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param sign        数字签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = hashID} 	：Hash标识
     * {@code string = pkValue} ：公钥值(国密时存在)
     */
    public UnionMessage servE40A(String pix, String bankID, String certVersion, String sign) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }
        if (UnionVerify.paramIsEmpty(sign)) {
            return UnionVerify.paramValueWrong("sign");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E40A");

        um.putBodyField("pix", pix);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);
        um.putBodyField("sign", sign);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E411 生成本行报文
     *
     * @param pix         应用ID
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param algFlag     签名算法
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = sign} 	：数字签名
     */
    public UnionMessage servE411(String pix, String bankID, String certVersion, String data, String algFlag) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(algFlag)) {
            return UnionVerify.paramValueWrong("algFlag");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E411");

        um.putBodyField("pix", pix);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);
        um.putBodyField("data", data);
        um.putBodyField("algFlag", algFlag);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E412 解析他行绑定报文
     *
     * @param pix         应用ID
     * @param mode        模式
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param sign        数字签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = certDN} 	：证书拥有者信息；
     * {@code string = certSN} 	：证书序列号；
     * {@code string = certEffdate} 	：证书起始时间；
     * {@code string = certInvdate} 	：证书结束时间；
     * {@code string = certBuf} 	：公钥证书。
     */
    public UnionMessage servE412(String pix, String mode, String bankID, String certVersion, String data, String sign) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E412");

        um.putBodyField("pix", pix);
        um.putBodyField("mode", mode);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);

        //判断可选
        if (!UnionVerify.paramIsEmpty(data)) {
            um.putBodyField("data", data);
        }
        if (!UnionVerify.paramIsEmpty(sign)) {
            um.putBodyField("sign", sign);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E413 数字签名加签
     *
     * @param pix         应用ID
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param algFlag     签名算法
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = sign} 	：数字签名
     */
    public UnionMessage servE413(String pix, String bankID, String certVersion, String data, String algFlag) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(algFlag)) {
            return UnionVerify.paramValueWrong("algFlag");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E413");

        um.putBodyField("pix", pix);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);
        um.putBodyField("data", data);
        um.putBodyField("algFlag", algFlag);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E414 数字签名核签
     *
     * @param pix         应用ID
     * @param bankID      参与行机构号
     * @param certVersion 证书版本号
     * @param data        签名数据
     * @param sign        数字签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE414(String pix, String bankID, String certVersion, String data, String sign) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pix)) {
            return UnionVerify.paramValueWrong("pix");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }
        if (UnionVerify.paramIsEmpty(certVersion)) {
            return UnionVerify.paramValueWrong("certVersion");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(sign)) {
            return UnionVerify.paramValueWrong("sign");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E414");

        um.putBodyField("pix", pix);
        um.putBodyField("bankID", bankID);
        um.putBodyField("certVersion", certVersion);
        um.putBodyField("data", data);
        um.putBodyField("sign", sign);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E416 加密文件
     *
     * @param pkName     公钥名称
     * @param vkName     私钥名称
     * @param fileName   文件名
     * @param version    加密数据版本号
     * @param bankID     机构编号
     * @param serverInfo 服务器信息
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE416(String pkName, String vkName, String fileName, String version, String bankID, String serverInfo) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pkName)) {
            return UnionVerify.paramValueWrong("pkName");
        }
        if (UnionVerify.paramIsEmpty(vkName)) {
            return UnionVerify.paramValueWrong("vkName");
        }
        if (UnionVerify.paramIsEmpty(fileName)) {
            return UnionVerify.paramValueWrong("fileName");
        }
        if (UnionVerify.paramIsEmpty(bankID)) {
            return UnionVerify.paramValueWrong("bankID");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E416");

        um.putBodyField("pkName", pkName);
        um.putBodyField("vkName", vkName);
        um.putBodyField("fileName", fileName);
        um.putBodyField("bankID", bankID);

        //判断可选
        if (!UnionVerify.paramIsEmpty(version)) {
            um.putBodyField("version", version);
        }
        if (!UnionVerify.paramIsEmpty(serverInfo)) {
            um.putBodyField("serverInfo", serverInfo);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E417 解密文件
     *
     * @param pkName   公钥名称
     * @param vkName   私钥名称
     * @param fileName 文件名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servE417(String pkName, String vkName, String fileName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(pkName)) {
            return UnionVerify.paramValueWrong("pkName");
        }
        if (UnionVerify.paramIsEmpty(vkName)) {
            return UnionVerify.paramValueWrong("vkName");
        }
        if (UnionVerify.paramIsEmpty(fileName)) {
            return UnionVerify.paramValueWrong("fileName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E417");

        um.putBodyField("pkName", pkName);
        um.putBodyField("vkName", vkName);
        um.putBodyField("fileName", fileName);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E601 计算安全报文
     *
     * @param mode             加密模式
     * @param scheme           方案标志
     * @param rootKeyName      根密钥名称
     * @param rootDiscreteData 根密钥离散数据q
     * @param pkType           保护密钥类型
     * @param protectKeyName   保护密钥名称
     * @param pkDiscreteNum    保护密钥离散次数
     * @param pkDiscreteData   保护密钥离散数据
     * @param sessionFlag      过程密钥标志
     * @param sessionFactor    过程因子
     * @param iv_cbc           IV-CBC
     * @param encFillData      加密填充数据
     * @param encFillOffset    加密填充偏移
     * @param iv_mac           IV-MAC
     * @param macFillData      MAC填充数据
     * @param macFillOffset    MAC填充偏移
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：MAC;
     * {@code string = data} 	：密文数据。
     */
    public UnionMessage servE601(String mode, String scheme, String rootKeyName, String rootDiscreteData, String pkType,
                                 String protectKeyName, String pkDiscreteNum, String pkDiscreteData, String sessionFlag, String sessionFactor,
                                 String iv_cbc, String encFillData, String encFillOffset, String iv_mac, String macFillData, String macFillOffset) {
        //校验参数
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(scheme)) {
            return UnionVerify.paramValueWrong("scheme");
        }
        if (UnionVerify.paramIsEmpty(rootKeyName)) {
            return UnionVerify.paramValueWrong("rootKeyName");
        }
        if (UnionVerify.paramIsEmpty(rootDiscreteData)) {
            return UnionVerify.paramValueWrong("rootDiscreteData");
        }
        if (UnionVerify.paramIsEmpty(pkType)) {
            return UnionVerify.paramValueWrong("pkType");
        }
        if (UnionVerify.paramIsEmpty(protectKeyName)) {
            return UnionVerify.paramValueWrong("protectKeyName");
        }
        if (UnionVerify.paramIsEmpty(pkDiscreteNum)) {
            return UnionVerify.paramValueWrong("pkDiscreteNum");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E601");

        um.putBodyField("mode", mode);
        um.putBodyField("scheme", scheme);
        um.putBodyField("rootKeyName", rootKeyName);
        um.putBodyField("rootDiscreteData", rootDiscreteData);
        um.putBodyField("pkType", pkType);
        um.putBodyField("protectKeyName", protectKeyName);
        um.putBodyField("pkDiscreteNum", pkDiscreteNum);

        //判断可选
        if (!UnionVerify.paramIsEmpty(pkDiscreteData)) {
            um.putBodyField("pkDiscreteData", pkDiscreteData);
        }
        if (!UnionVerify.paramIsEmpty(sessionFlag)) {
            um.putBodyField("sessionFlag", sessionFlag);
        }
        if (!UnionVerify.paramIsEmpty(sessionFactor)) {
            um.putBodyField("sessionFactor", sessionFactor);
        }
        if (!UnionVerify.paramIsEmpty(iv_cbc)) {
            um.putBodyField("iv_cbc", iv_cbc);
        }
        if (!UnionVerify.paramIsEmpty(encFillData)) {
            um.putBodyField("encFillData", encFillData);
        }
        if (!UnionVerify.paramIsEmpty(encFillOffset)) {
            um.putBodyField("encFillOffset", encFillOffset);
        }
        if (!UnionVerify.paramIsEmpty(iv_mac)) {
            um.putBodyField("iv_mac", iv_mac);
        }
        if (!UnionVerify.paramIsEmpty(macFillData)) {
            um.putBodyField("macFillData", macFillData);
        }
        if (!UnionVerify.paramIsEmpty(macFillOffset)) {
            um.putBodyField("macFillOffset", macFillOffset);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E602 使用离散密钥加解密数据或计算MAC
     *
     * @param keyIndex           密钥索引
     * @param keyName            密钥名称
     * @param keyDiscreteData    密钥离散数据
     * @param keyDiscreteNum     密钥离散次数
     * @param algorithmID        过程密钥算法标志
     * @param GC_Data            过程密钥离散数据
     * @param encryptFlag        加密标志
     * @param encryptAlgorithmID 加密算法选择
     * @param macFlag            MAC 标志
     * @param macAlgorithmID     MAC 算法选择
     * @param data               报文
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：MAC;
     * {@code string = data} 	：报文密文/明文.
     */
    public UnionMessage servE602(String keyIndex, String keyName, String keyDiscreteData, String keyDiscreteNum, String algorithmID,
                                 String GC_Data, String encryptFlag, String encryptAlgorithmID, String macFlag, String macAlgorithmID, String data) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyDiscreteData)) {
            return UnionVerify.paramValueWrong("keyDiscreteData");
        }
        if (UnionVerify.paramIsEmpty(encryptFlag)) {
            return UnionVerify.paramValueWrong("encryptFlag");
        }
        if (UnionVerify.paramIsEmpty(macFlag)) {
            return UnionVerify.paramValueWrong("macFlag");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E602");

        um.putBodyField("keyDiscreteData", keyDiscreteData);
        um.putBodyField("encryptFlag", encryptFlag);
        um.putBodyField("macFlag", macFlag);
        um.putBodyField("data", data);

        //判断可选
        if (!UnionVerify.paramIsEmpty(keyIndex)) {
            um.putBodyField("keyIndex", keyIndex);
        }
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(keyDiscreteNum)) {
            um.putBodyField("keyDiscreteNum", keyDiscreteNum);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(GC_Data)) {
            um.putBodyField("GC_Data", GC_Data);
        }
        if (!UnionVerify.paramIsEmpty(encryptAlgorithmID)) {
            um.putBodyField("encryptAlgorithmID", encryptAlgorithmID);
        }
        if (!UnionVerify.paramIsEmpty(macAlgorithmID)) {
            um.putBodyField("macAlgorithmID", macAlgorithmID);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E603 使用离散密钥转加密PIN
     *
     * @param inScheme        导入方案
     * @param inGC_Data       过程数据（导入）
     * @param inKeyFlag       导入密钥标志
     * @param inKeyIndex      加密机内部导入密钥
     * @param inKeyName       导入密钥名称
     * @param inDiscreteNum   密钥离散次数(导入)
     * @param inDiscreteData  密钥离散数据(导入)
     * @param outScheme       导出方案
     * @param outGC_Data      过程数据（导出）
     * @param outKeyFlag      导出密钥标志
     * @param outKeyIndex     加密机内部导出密钥
     * @param outKeyName      导出密钥名称
     * @param outDiscreteNum  密钥离散次数(导出)
     * @param outDiscreteData 密钥离散数据（导出）
     * @param oriAccNo        原账号
     * @param desAccNo        目的账号
     * @param oriPIN          源PIN块格式
     * @param desPIN          目的PIN块格式
     * @param accNo           目的全帐号
     * @param oriPINBlock     源PINBLOCK
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = desPINBlock} 	：目标PIN
     */
    public UnionMessage servE603(String inScheme, String inGC_Data, String inKeyFlag, String inKeyIndex, String inKeyName,
                                 String inDiscreteNum, String inDiscreteData, String outScheme, String outGC_Data, String outKeyFlag, String outKeyIndex,
                                 String outKeyName, String outDiscreteNum, String outDiscreteData, String oriAccNo, String desAccNo, String oriPIN,
                                 String desPIN, String accNo, String oriPINBlock) {
        //校验参数
        if (UnionVerify.paramIsEmpty(inScheme)) {
            return UnionVerify.paramValueWrong("inScheme");
        }
        if (UnionVerify.paramIsEmpty(inKeyFlag)) {
            return UnionVerify.paramValueWrong("inKeyFlag");
        }
        if (UnionVerify.paramIsEmpty(inDiscreteNum)) {
            return UnionVerify.paramValueWrong("inDiscreteNum");
        }
        if (UnionVerify.paramIsEmpty(inDiscreteData)) {
            return UnionVerify.paramValueWrong("inDiscreteData");
        }
        if (UnionVerify.paramIsEmpty(outScheme)) {
            return UnionVerify.paramValueWrong("outScheme");
        }
        if (UnionVerify.paramIsEmpty(outKeyFlag)) {
            return UnionVerify.paramValueWrong("outKeyFlag");
        }
        if (UnionVerify.paramIsEmpty(outDiscreteNum)) {
            return UnionVerify.paramValueWrong("outDiscreteNum");
        }
        if (UnionVerify.paramIsEmpty(outDiscreteData)) {
            return UnionVerify.paramValueWrong("outDiscreteData");
        }
        if (UnionVerify.paramIsEmpty(oriAccNo)) {
            return UnionVerify.paramValueWrong("oriAccNo");
        }
        if (UnionVerify.paramIsEmpty(desAccNo)) {
            return UnionVerify.paramValueWrong("desAccNo");
        }
        if (UnionVerify.paramIsEmpty(oriPIN)) {
            return UnionVerify.paramValueWrong("oriPIN");
        }
        if (UnionVerify.paramIsEmpty(desPIN)) {
            return UnionVerify.paramValueWrong("desPIN");
        }
        if (UnionVerify.paramIsEmpty(oriPINBlock)) {
            return UnionVerify.paramValueWrong("oriPINBlock");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E603");
        um.putBodyField("inScheme", inScheme);
        um.putBodyField("inKeyFlag", inKeyFlag);
        um.putBodyField("inDiscreteNum", inDiscreteNum);
        um.putBodyField("inDiscreteData", inDiscreteData);
        um.putBodyField("outScheme", outScheme);
        um.putBodyField("outKeyFlag", outKeyFlag);
        um.putBodyField("outDiscreteNum", outDiscreteNum);
        um.putBodyField("outDiscreteData", outDiscreteData);
        um.putBodyField("oriAccNo", oriAccNo);
        um.putBodyField("desAccNo", desAccNo);
        um.putBodyField("oriPIN", oriPIN);
        um.putBodyField("desPIN", desPIN);
        um.putBodyField("oriPINBlock", oriPINBlock);

        //判断可选
        if (!UnionVerify.paramIsEmpty(inGC_Data)) {
            um.putBodyField("inGC_Data", inGC_Data);
        }
        if (!UnionVerify.paramIsEmpty(inKeyIndex)) {
            um.putBodyField("inKeyIndex", inKeyIndex);
        }
        if (!UnionVerify.paramIsEmpty(inKeyName)) {
            um.putBodyField("inKeyName", inKeyName);
        }
        if (!UnionVerify.paramIsEmpty(outGC_Data)) {
            um.putBodyField("outGC_Data", outGC_Data);
        }
        if (!UnionVerify.paramIsEmpty(outKeyIndex)) {
            um.putBodyField("outKeyIndex", outKeyIndex);
        }
        if (!UnionVerify.paramIsEmpty(outKeyName)) {
            um.putBodyField("outKeyName", outKeyName);
        }
        if (!UnionVerify.paramIsEmpty(accNo)) {
            um.putBodyField("accNo", accNo);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E801 根据离散数据生成MAC/TAC
     *
     * @param keyName      密钥名称
     * @param keyIndex     密钥索引
     * @param algorithmID  算法标识
     * @param mode         模式
     * @param discreteNum  离散次数
     * @param discreteData 离散数据
     * @param sessionData  过程数据
     * @param data         数据
     * @param iv           初始向量
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：MAC;
     * {@code string = Icv} 	：Icv.
     */
    public UnionMessage servE801(String keyName, String keyIndex, String algorithmID, String mode, String discreteNum,
                                 String discreteData, String sessionData, String data, String iv) {
        //校验参数
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(discreteNum)) {
            return UnionVerify.paramValueWrong("discreteNum");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(iv)) {
            return UnionVerify.paramValueWrong("iv");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E801");
        um.putBodyField("mode", mode);
        um.putBodyField("discreteNum", discreteNum);
        um.putBodyField("data", data);
        um.putBodyField("iv", iv);

        //判断可选
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(keyIndex)) {
            um.putBodyField("keyIndex", keyIndex);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(discreteData)) {
            um.putBodyField("discreteData", discreteData);
        }
        if (!UnionVerify.paramIsEmpty(sessionData)) {
            um.putBodyField("sessionData", sessionData);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E802 产生运通卡安全码
     *
     * @param keyName     密钥名称
     * @param mode        模式标识
     * @param accNo       账号
     * @param serviceCode 服务码
     * @param activeDate  有效期
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mode} 	：模式标识
     * {@code string = safeCodeFive} 	：5位卡安全码
     * {@code string = safeCodeFour} 	：4位卡安全码
     * {@code string = safeCodeThree} 	：3位卡安全码
     */
    public UnionMessage servE802(String keyName, String mode, String accNo, String serviceCode, String activeDate) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(activeDate)) {
            return UnionVerify.paramValueWrong("activeDate");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E802");
        um.putBodyField("keyName", keyName);
        um.putBodyField("mode", mode);
        um.putBodyField("accNo", accNo);
        um.putBodyField("activeDate", activeDate);

        //判断可选
        if (!UnionVerify.paramIsEmpty(serviceCode)) {
            um.putBodyField("serviceCode", serviceCode);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E803 校验运通卡安全码
     *
     * @param keyName       密钥名称
     * @param mode          模式标识
     * @param accNo         账号
     * @param serviceCode   服务码
     * @param activeDate    有效期
     * @param safeCodeFive  5位卡安全码
     * @param safeCodeFour  4位卡安全码
     * @param safeCodeThree 3位卡安全码
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mode} 	：模式标识;
     * {@code string = checkResult} 	：安全码校验结果.
     */
    public UnionMessage servE803(String keyName, String mode, String accNo, String serviceCode, String activeDate,
                                 String safeCodeFive, String safeCodeFour, String safeCodeThree) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(activeDate)) {
            return UnionVerify.paramValueWrong("activeDate");
        }
        if (UnionVerify.paramIsEmpty(safeCodeThree)) {
            return UnionVerify.paramValueWrong("safeCodeThree");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E803");
        um.putBodyField("keyName", keyName);
        um.putBodyField("mode", mode);
        um.putBodyField("accNo", accNo);
        um.putBodyField("activeDate", activeDate);
        um.putBodyField("safeCodeThree", safeCodeThree);

        //判断可选
        if (!UnionVerify.paramIsEmpty(serviceCode)) {
            um.putBodyField("serviceCode", serviceCode);
        }
        if (!UnionVerify.paramIsEmpty(safeCodeFive)) {
            um.putBodyField("safeCodeFive", safeCodeFive);
        }
        if (!UnionVerify.paramIsEmpty(safeCodeFour)) {
            um.putBodyField("safeCodeFour", safeCodeFour);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E804 验证离散数据生成MAC/TAC
     *
     * @param keyName      密钥名称
     * @param keyIndex     密钥索引
     * @param algorithmID  算法标识
     * @param mode         模式
     * @param discreteNum  离散次数
     * @param discreteData 离散数据
     * @param sessionData  过程数据
     * @param data         数据
     * @param iv           初始向量
     * @param mac          mac
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：Mac
     */
    public UnionMessage servE804(String keyName, String keyIndex, String algorithmID, String mode, String discreteNum,
                                 String discreteData, String sessionData, String data, String iv, String mac) {
        //校验参数
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(discreteNum)) {
            return UnionVerify.paramValueWrong("discreteNum");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(iv)) {
            return UnionVerify.paramValueWrong("iv");
        }
        if (UnionVerify.paramIsEmpty(mac)) {
            return UnionVerify.paramValueWrong("mac");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E804");
        um.putBodyField("mode", mode);
        um.putBodyField("discreteNum", discreteNum);
        um.putBodyField("data", data);
        um.putBodyField("iv", iv);
        um.putBodyField("mac", mac);

        //判断可选
        if (!UnionVerify.paramIsEmpty(keyName)) {
            um.putBodyField("keyName", keyName);
        }
        if (!UnionVerify.paramIsEmpty(keyIndex)) {
            um.putBodyField("keyIndex", keyIndex);
        }
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }

        if (!UnionVerify.paramIsEmpty(discreteData)) {
            um.putBodyField("discreteData", discreteData);
        }
        if (!UnionVerify.paramIsEmpty(sessionData)) {
            um.putBodyField("sessionData", sessionData);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * E805 生成并验证CVC3
     *
     * @param mode      模式
     * @param keyName   主密钥名称
     * @param pan       卡号或（卡号+卡序列号）
     * @param trackData
     * @param un        终端授权码
     * @param atc       离散过程因子
     * @param CVC3      CVC3
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = CVC3} 	：CVC3
     */
    public UnionMessage servE805(String mode, String keyName, String pan, String trackData,
                                 String un, String atc, String CVC3) {
        //校验参数
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(pan)) {
            return UnionVerify.paramValueWrong("pan");
        }
        if (UnionVerify.paramIsEmpty(trackData)) {
            return UnionVerify.paramValueWrong("trackData");
        }
        if (UnionVerify.paramIsEmpty(un)) {
            return UnionVerify.paramValueWrong("un");
        }
        if (UnionVerify.paramIsEmpty(atc)) {
            return UnionVerify.paramValueWrong("atc");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "E805");
        um.putBodyField("mode", mode);
        um.putBodyField("keyName", keyName);
        um.putBodyField("pan", pan);
        um.putBodyField("trackData", trackData);
        um.putBodyField("un", un);
        um.putBodyField("atc", atc);

        //判断可选
        if (!UnionVerify.paramIsEmpty(CVC3)) {
            um.putBodyField("CVC3", CVC3);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EEGD 验证TAC指令（UB/UC）
     *
     * @param mode        模式标识
     * @param caclMacMode 计算MAC模式
     * @param keyName     mk-ac密钥名称
     * @param version     密钥的版本号
     * @param dvsData     卡号+序号
     * @param atc         离散过程因子
     * @param fillMode    填充标识
     * @param macData     计算MAC数据
     * @param mac         待校验的mac值
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：mac值
     */
    public UnionMessage servEEGD(String mode, String caclMacMode, String keyName, String version,
                                 String dvsData, String atc, String fillMode, String macData, String mac) {

        //校验参数
        if (UnionVerify.paramIsEmpty(mode)) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(version)) {
            return UnionVerify.paramValueWrong("version");
        }
        if (UnionVerify.paramIsEmpty(dvsData)) {
            return UnionVerify.paramValueWrong("dvsData");
        }
        if (UnionVerify.paramIsEmpty(macData)) {
            return UnionVerify.paramValueWrong("macData");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EEGD");
        um.putBodyField("mode", mode);
        um.putBodyField("keyName", keyName);
        um.putBodyField("version", version);
        um.putBodyField("dvsData", dvsData);
        um.putBodyField("macData", macData);

        //判断可选
        if (!UnionVerify.paramIsEmpty(caclMacMode)) {
            um.putBodyField("caclMacMode", caclMacMode);
        }
        if (!UnionVerify.paramIsEmpty(atc)) {
            um.putBodyField("atc", atc);
        }
        if (!UnionVerify.paramIsEmpty(fillMode)) {
            um.putBodyField("fillMode", fillMode);
        }
        if (!UnionVerify.paramIsEmpty(mac)) {
            um.putBodyField("mac", mac);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EEGK 验证TC指令（KW/KX）
     *
     * @param keyName  mk-ac密钥名称
     * @param version  密钥的版本号
     * @param pan      卡号或（卡号+卡序列号）
     * @param atc      离散过程因子
     * @param arqcData 计算ARQC使用的数据
     * @param arqc     待验证的TC
     * @param iccType  Ic卡类型
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servEEGK(String keyName, String version, String pan, String atc, String arqcData,
                                 String arqc, String iccType) {

        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(version)) {
            return UnionVerify.paramValueWrong("version");
        }
        if (UnionVerify.paramIsEmpty(pan)) {
            return UnionVerify.paramValueWrong("pan");
        }
        if (UnionVerify.paramIsEmpty(atc)) {
            return UnionVerify.paramValueWrong("atc");
        }
        if (UnionVerify.paramIsEmpty(arqcData)) {
            return UnionVerify.paramValueWrong("arqcData");
        }
        if (UnionVerify.paramIsEmpty(arqc)) {
            return UnionVerify.paramValueWrong("arqc");
        }
        if (UnionVerify.paramIsEmpty(iccType)) {
            return UnionVerify.paramValueWrong("iccType");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EEGK");
        um.putBodyField("keyName", keyName);
        um.putBodyField("version", version);
        um.putBodyField("pan", pan);
        um.putBodyField("atc", atc);
        um.putBodyField("arqcData", arqcData);
        um.putBodyField("arqc", arqc);
        um.putBodyField("iccType", iccType);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EF91 ZPK加密明文字符密码（516）
     *
     * @param zpkKeyName zpk密钥名称
     * @param plainPin   PIN明文
     * @param accNo      账号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock} 	：pin密文
     */
    public UnionMessage servEF91(String zpkKeyName, String plainPin, String accNo) {

        //校验参数
        if (UnionVerify.paramIsEmpty(zpkKeyName)) {
            return UnionVerify.paramValueWrong("zpkKeyName");
        }
        if (UnionVerify.paramIsEmpty(plainPin)) {
            return UnionVerify.paramValueWrong("plainPin");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EE91");
        um.putBodyField("zpkKeyName", zpkKeyName);
        um.putBodyField("plainPin", plainPin);
        um.putBodyField("accNo", accNo);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EF92 把zpk加密的字符密码转换为另一zpk加密（支持两个账号）（517）
     *
     * @param zpkKeyName1 源zpk密钥名称
     * @param zpkKeyName2 目的zpk密钥名
     * @param pinBlock1   源PIN密文
     * @param accNo1      源账号
     * @param accNo2      目的账号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock2} 	：目的PIN密文
     */
    public UnionMessage servEF92(String zpkKeyName1, String zpkKeyName2, String pinBlock1, String accNo1, String accNo2) {
        //校验参数
        if (UnionVerify.paramIsEmpty(zpkKeyName1)) {
            return UnionVerify.paramValueWrong("zpkKeyName1");
        }
        if (UnionVerify.paramIsEmpty(zpkKeyName2)) {
            return UnionVerify.paramValueWrong("zpkKeyName2");
        }
        if (UnionVerify.paramIsEmpty(pinBlock1)) {
            return UnionVerify.paramValueWrong("pinBlock1");
        }
        if (UnionVerify.paramIsEmpty(accNo1)) {
            return UnionVerify.paramValueWrong("accNo1");
        }
        if (UnionVerify.paramIsEmpty(accNo2)) {
            return UnionVerify.paramValueWrong("accNo2");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EE92");
        um.putBodyField("zpkKeyName1", zpkKeyName1);
        um.putBodyField("zpkKeyName2", zpkKeyName2);
        um.putBodyField("pinBlock1", pinBlock1);
        um.putBodyField("accNo1", accNo1);
        um.putBodyField("accNo2", accNo2);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EF93 ZPK解密密文文字符密码（518）
     *
     * @param zpkKeyName Zpk密钥名称
     * @param pinBlock   PIN密文
     * @param accNo      账号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = plainPin} 	：PIN明文
     */
    public UnionMessage servEF93(String zpkKeyName, String pinBlock, String accNo) {
        //校验参数
        if (UnionVerify.paramIsEmpty(zpkKeyName)) {
            return UnionVerify.paramValueWrong("zpkKeyName");
        }
        if (UnionVerify.paramIsEmpty(pinBlock)) {
            return UnionVerify.paramValueWrong("pinBlock");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EE93");
        um.putBodyField("zpkKeyName", zpkKeyName);
        um.putBodyField("pinBlock", pinBlock);
        um.putBodyField("accNo", accNo);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EF94 将公钥加密的PIN转换为ZEK/EDK加密（519）
     *
     * @param vkIndex      私钥索引
     * @param algorithmID  公钥加密算法标识
     * @param keyName      目的密钥
     * @param dataFillMode 公钥加密填充模式
     * @param pinByPK      公钥加密的PIN
     * @param userName     网银用户名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = pinBlock} 	：目的密钥加密密文
     */
    public UnionMessage servEF94(String vkIndex, String algorithmID, String keyName, String dataFillMode, String pinByPK, String userName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(vkIndex)) {
            return UnionVerify.paramValueWrong("vkIndex");
        }
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(pinByPK)) {
            return UnionVerify.paramValueWrong("pinByPK");
        }
        if (UnionVerify.paramIsEmpty(userName)) {
            return UnionVerify.paramValueWrong("userName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EE94");
        um.putBodyField("vkIndex", vkIndex);
        um.putBodyField("keyName", keyName);
        um.putBodyField("pinByPK", pinByPK);
        um.putBodyField("userName", userName);

        //判断可选
        if (!UnionVerify.paramIsEmpty(algorithmID)) {
            um.putBodyField("algorithmID", algorithmID);
        }
        if (!UnionVerify.paramIsEmpty(dataFillMode)) {
            um.putBodyField("dataFillMode", dataFillMode);
        }

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EF95 将ZPK加密的PIN密文转成指定密钥加密输出(520)
     *
     * @param zpkKeyName ZPK密钥名称
     * @param keyName    目的密钥名
     * @param pinByPK    ZPK密钥加密的PIN密文
     * @param format     源PIN块格式
     * @param accNo      账号
     * @param userName   网络用户名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = cipherData} 	：密文
     */
    public UnionMessage servEF95(String zpkKeyName, String keyName, String pinByPK, String format, String accNo, String userName) {
        //校验参数
        if (UnionVerify.paramIsEmpty(zpkKeyName)) {
            return UnionVerify.paramValueWrong("zpkKeyName");
        }
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(pinByPK)) {
            return UnionVerify.paramValueWrong("pinByPK");
        }
        if (UnionVerify.paramIsEmpty(format)) {
            return UnionVerify.paramValueWrong("format");
        }
        if (UnionVerify.paramIsEmpty(accNo)) {
            return UnionVerify.paramValueWrong("accNo");
        }
        if (UnionVerify.paramIsEmpty(userName)) {
            return UnionVerify.paramValueWrong("userName");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EE95");
        um.putBodyField("zpkKeyName", zpkKeyName);
        um.putBodyField("keyName", keyName);
        um.putBodyField("pinByPK", pinByPK);
        um.putBodyField("format", format);
        um.putBodyField("accNo", accNo);
        um.putBodyField("userName", userName);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EF96 将一个ZEK/EDK密钥加密的数据转为另一ZEK/EDK密钥加密（521）
     *
     * @param keyName1
     * @param keyName2
     * @param cipherData1
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = cipherData2} 	：目的密钥加密的数据的密文
     */
    public UnionMessage servEF96(String keyName1, String keyName2, String cipherData1) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName1)) {
            return UnionVerify.paramValueWrong("keyName1");
        }
        if (UnionVerify.paramIsEmpty(keyName2)) {
            return UnionVerify.paramValueWrong("keyName2");
        }
        if (UnionVerify.paramIsEmpty(cipherData1)) {
            return UnionVerify.paramValueWrong("cipherData1");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EE96");
        um.putBodyField("keyName1", keyName1);
        um.putBodyField("keyName2", keyName2);
        um.putBodyField("cipherData1", cipherData1);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EFA1使用会话密钥对明文数据加密 (780)（仅支持国际）
     *
     * @param keyName   密钥名称
     * @param encFlag   加密模式
     * @param ivData    初始向量
     * @param dispData1 离散数据1
     * @param dispData2 离散数据2
     * @param plainData 明文数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = cipherData} 	：密文数据
     */
    public UnionMessage servEFA1(String keyName, String encFlag, String ivData, String dispData1, String dispData2, String plainData) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(encFlag)) {
            return UnionVerify.paramValueWrong("encFlag");
        }
        if (UnionVerify.paramIsEmpty(ivData)) {
            return UnionVerify.paramValueWrong("ivData");
        }

        if (UnionVerify.paramIsEmpty(dispData1)) {
            return UnionVerify.paramValueWrong("dispData1");
        }
        if (UnionVerify.paramIsEmpty(dispData2)) {
            return UnionVerify.paramValueWrong("dispData2");
        }
        if (UnionVerify.paramIsEmpty(plainData)) {
            return UnionVerify.paramValueWrong("plainData");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EFA1");
        um.putBodyField("keyName", keyName);
        um.putBodyField("encFlag", encFlag);
        um.putBodyField("ivData", ivData);
        um.putBodyField("dispData1", dispData1);
        um.putBodyField("dispData2", dispData2);
        um.putBodyField("plainData", plainData);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EFA2使用会话密钥对MAC数据计算C-MAC (781)（仅支持国际）
     *
     * @param keyName   密钥名称
     * @param ivData1   初始向量1
     * @param ivData1   初始向量2
     * @param dispData1 离散数据1
     * @param dispData2 离散数据2
     * @param macData   mac数据
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = mac} 	：MAC数据;
     * {@code string = icv} 	：ICV数据
     */
    public UnionMessage servEFA2(String keyName, String ivData1, String ivData2, String dispData1, String dispData2, String macData) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(ivData1)) {
            return UnionVerify.paramValueWrong("ivData1");
        }
        if (UnionVerify.paramIsEmpty(ivData2)) {
            return UnionVerify.paramValueWrong("ivData2");
        }

        if (UnionVerify.paramIsEmpty(dispData1)) {
            return UnionVerify.paramValueWrong("dispData1");
        }
        if (UnionVerify.paramIsEmpty(dispData2)) {
            return UnionVerify.paramValueWrong("dispData2");
        }
        if (UnionVerify.paramIsEmpty(macData)) {
            return UnionVerify.paramValueWrong("macData");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EFA2");
        um.putBodyField("keyName", keyName);
        um.putBodyField("ivData1", ivData1);
        um.putBodyField("ivData2", ivData2);
        um.putBodyField("dispData1", dispData1);
        um.putBodyField("dispData2", dispData2);
        um.putBodyField("macData", macData);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EFA3使用会话密钥对另一把过程密钥进行加密输出 (782)（仅支持国际）
     *
     * @param keyName1 密钥名称1
     * @param keyName2 密钥名称2
     * @param prodate1 离散数据1
     * @param prodate2 离散数据2
     * @param prodate3 离散数据3
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：
     * {@code string = keyByZMK} 	：目的密钥密文;
     * {@code string = checkValue} 	：目的密钥校验值
     */
    public UnionMessage servEFA3(String keyName1, String keyName2, String prodate1, String prodate2, String prodate3) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName1)) {
            return UnionVerify.paramValueWrong("keyName1");
        }
        if (UnionVerify.paramIsEmpty(keyName2)) {
            return UnionVerify.paramValueWrong("keyName2");
        }
        if (UnionVerify.paramIsEmpty(prodate1)) {
            return UnionVerify.paramValueWrong("prodate1");
        }
        if (UnionVerify.paramIsEmpty(prodate2)) {
            return UnionVerify.paramValueWrong("prodate2");
        }
        if (UnionVerify.paramIsEmpty(prodate3)) {
            return UnionVerify.paramValueWrong("prodate3");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EFA3");
        um.putBodyField("keyName1", keyName1);
        um.putBodyField("keyName2", keyName2);
        um.putBodyField("prodate1", prodate1);
        um.putBodyField("prodate2", prodate2);
        um.putBodyField("prodate3", prodate3);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    public UnionMessage servEFA4(String keyName1, String keyName2, String prodate1, String prodate2, String prodate3) {
        //校验参数
        if (UnionVerify.paramIsEmpty(keyName1)) {
            return UnionVerify.paramValueWrong("keyName1");
        }
        if (UnionVerify.paramIsEmpty(keyName2)) {
            return UnionVerify.paramValueWrong("keyName2");
        }
        if (UnionVerify.paramIsEmpty(prodate1)) {
            return UnionVerify.paramValueWrong("prodate1");
        }
        if (UnionVerify.paramIsEmpty(prodate2)) {
            return UnionVerify.paramValueWrong("prodate2");
        }
        if (UnionVerify.paramIsEmpty(prodate3)) {
            return UnionVerify.paramValueWrong("prodate3");
        }

        //组装报文
        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EFA4");
        um.putBodyField("keyName1", keyName1);
        um.putBodyField("keyName2", keyName2);
        um.putBodyField("prodate1", prodate1);
        um.putBodyField("prodate2", prodate2);
        um.putBodyField("prodate3", prodate3);

        //发送、返回
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EA30 数字签名
     *
     * @param keyName 密钥名称
     * @param userID  用户标识
     * @param data    签名数据
     * @return sign                签名结果
     */
    public UnionMessage servEA30(String keyName, String userID, String data) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "EA30");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("data", data);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(userID)) {
            um.putBodyField("userID", userID);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA31 数字验签
     *
     * @param keyName 密钥名称
     * @param userID  用户标识
     * @param data    签名数据
     * @param sign    签名结果
     * @return
     */
    public UnionMessage servEA31(String keyName, String userID, String data, String sign) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (UnionVerify.paramIsEmpty(sign)) {
            return UnionVerify.paramValueWrong("sign");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "EA31");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("data", data);
        um.putBodyField("sign", sign);

        //判断可选项，存在则组装进报文体
        if (!UnionVerify.paramIsEmpty(userID)) {
            um.putBodyField("userID", userID);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA32 敏感信息加密
     *
     * @param keyName  密钥名称
     * @param sensInfo 敏感信息
     * @return encInfo        保护密钥加密的敏感信息
     */
    public UnionMessage servEA32(String keyName, String sensInfo) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(sensInfo)) {
            return UnionVerify.paramValueWrong("sensInfo");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "EA32");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("sensInfo", sensInfo);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA33 敏感信息解密
     *
     * @param keyName 密钥名称
     * @param encKey  公钥加密的保护密钥
     * @param encInfo 保护密钥加密的敏感信息
     * @return sensInfo        敏感信息明文
     */
    public UnionMessage servEA33(String keyName, String encKey, String encInfo) {
        //判断必填项
        if (UnionVerify.paramIsEmpty(keyName)) {
            return UnionVerify.paramValueWrong("keyName");
        }
        if (UnionVerify.paramIsEmpty(encKey)) {
            return UnionVerify.paramValueWrong("encKey");
        }
        if (UnionVerify.paramIsEmpty(encInfo)) {
            return UnionVerify.paramValueWrong("encInfo");
        }

        UnionMessage um = new UnionRequest();
        //组装服务码
        um.putHeadField("serviceCode", "EA33");

        //组装报文体
        um.putBodyField("keyName", keyName);
        um.putBodyField("encKey", encKey);
        um.putBodyField("encInfo", encInfo);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA10查询数字证书算法和公钥值
     *
     * @param serialNum 证书序列号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = algFlag} 	：算法标识;<br>
     * {@code string = pkValue} ：公钥值
     */
    public UnionMessage servEA10(String serialNum) {
        if (UnionVerify.paramIsEmpty(serialNum)) {
            return UnionVerify.paramValueWrong("serialNum");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA10");
        um.putBodyField("serialNum", serialNum);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA11数字签名加签
     *
     * @param serialNum 证书序列号
     * @param data      待签名数据
     * @param dataType  data数据类型
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = sign} 	：签名结果
     */
    public UnionMessage servEA11(String serialNum, String data, int dataType) {
        if (UnionVerify.paramIsEmpty(serialNum)) {
            return UnionVerify.paramValueWrong("serialNum");
        }

        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        if (dataType != 0 && dataType != 1) {
            return UnionVerify.paramValueWrong("dataType");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA11");
        um.putBodyField("serialNum", serialNum);
        um.putBodyField("data", data);

        if (dataType == 0) {
            um.putBodyField("dataType", 1);
        } else {
            um.putBodyField("dataType", dataType);
        }

        //改变编码
        // Update by linxl 2017-09-26 解决网联服务输入数据为UTF-8的中文时，输出中文时会乱码
        if (UnionUtil.needToChangeEncodeUTF8(dataType + "", null)) {
            um = UnionUtil.changeEncodeUTF8(um, null, false);
        }
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA12数字签名核签
     *
     * @param serialNum 证书序列号
     * @param data      待签名数据
     * @param dataType  data数据类型
     * @param sign      签名
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     */
    public UnionMessage servEA12(String serialNum, String data, int dataType, String sign) {
        if (UnionVerify.paramIsEmpty(serialNum)) {
            return UnionVerify.paramValueWrong("serialNum");
        }

        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        if (UnionVerify.paramIsEmpty(sign)) {
            return UnionVerify.paramValueWrong("sign");
        }

        if (dataType != 0 && dataType != 1) {
            return UnionVerify.paramValueWrong("dataType");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA12");
        um.putBodyField("serialNum", serialNum);
        um.putBodyField("data", data);

        if (dataType == 0) {
            um.putBodyField("dataType", 1);
        } else {
            um.putBodyField("dataType", dataType);
        }
        um.putBodyField("sign", sign);

        //改变编码
        // Update by linxl 2017-09-26 解决网联服务输入数据为UTF-8的中文时，输出中文时会乱码
        if (UnionUtil.needToChangeEncodeUTF8(dataType + "", null)) {
            um = UnionUtil.changeEncodeUTF8(um, null, false);
        }
        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA13公钥加密对称密钥
     *
     * @param mode      密钥模式
     * @param keyValue  密钥值
     * @param keyType   密钥算法类型
     * @param serialNum 证书序列号
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = keyByPK} 	：公钥加密的密钥密文<br>
     * {@code string = keyValue} 	：密钥值
     */
    public UnionMessage servEA13(int mode, String keyValue, String keyType, String serialNum) {
        if (UnionVerify.paramIsEmpty(serialNum)) {
            return UnionVerify.paramValueWrong("serialNum");
        }
        if (UnionVerify.paramIsEmpty(keyType)) {
            return UnionVerify.paramValueWrong("keyType");
        }
        if (mode != 0 && mode != 1) {
            return UnionVerify.paramValueWrong("mode");
        }
        if (mode == 1 && UnionVerify.paramIsEmpty(keyValue)) {
            return UnionVerify.paramValueWrong("keyValue");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA13");
        um.putBodyField("serialNum", serialNum);
        um.putBodyField("mode", mode);
        if (mode == 1) {
            um.putBodyField("keyValue", keyValue);
        }
        um.putBodyField("keyType", keyType);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA14公钥加密的对称密钥转lmk加密
     *
     * @param serialNum 证书序列号
     * @param keyByPK   公钥加密的密文
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = keyValue} 	：密钥密文<br>
     * {@code string = keyType} 	：密钥类型
     */
    public UnionMessage servEA14(String serialNum, String keyByPK) {
        if (UnionVerify.paramIsEmpty(serialNum)) {
            return UnionVerify.paramValueWrong("serialNum");
        }
        if (UnionVerify.paramIsEmpty(keyByPK)) {
            return UnionVerify.paramValueWrong("keyByPK");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA14");
        um.putBodyField("serialNum", serialNum);
        um.putBodyField("keyByPK", keyByPK);

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA15	数据加密
     *
     * @param keyValue    密钥值
     * @param keyType     密钥类型
     * @param paddingFlag 填充标识
     * @param data        待加密的数据
     * @param dataType    data数据类型
     * @param algorithmID 算法标识
     * @param iv          iv向量
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = data} 	：数据密文
     */
    public UnionMessage servEA15(String keyValue, String keyType, int paddingFlag,
                                 String data, int dataType, int algorithmID, String iv) {
        if (UnionVerify.paramIsEmpty(keyValue)) {
            return UnionVerify.paramValueWrong("keyValue");
        }
        if (UnionVerify.paramIsEmpty(keyType)) {
            return UnionVerify.paramValueWrong("keyType");
        }
        if (paddingFlag != 0 && paddingFlag != 1) {
            return UnionVerify.paramValueWrong("paddingFlag");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (dataType != 0 && dataType != 1) {
            return UnionVerify.paramValueWrong("dataType");
        }
        if (algorithmID != 0 && algorithmID != 1) {
            return UnionVerify.paramValueWrong("algorithmID");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA15");
        um.putBodyField("keyValue", keyValue);
        um.putBodyField("keyType", keyType);
        um.putBodyField("paddingFlag", paddingFlag);
        um.putBodyField("data", data);

        if (dataType == 0) {
            um.putBodyField("dataType", 1);
        } else {
            um.putBodyField("dataType", dataType);
        }

        um.putBodyField("algorithmID", algorithmID);
        if (algorithmID == 1) {
            um.putBodyField("iv", iv);
        }

        //改变编码
        // Update by linxl 2017-09-26 解决网联服务输入数据为UTF-8的中文时，输出中文时会乱码
        if (UnionUtil.needToChangeEncodeUTF8(dataType + "", null)) {
            um = UnionUtil.changeEncodeUTF8(um, null, false);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }

    /**
     * EA16	数据解密
     *
     * @param keyValue    密钥值
     * @param keyType     密钥类型
     * @param paddingFlag 填充标识
     * @param data        密文数据
     * @param exportFlag  输出明文数据标识
     * @param algorithmID 算法标识
     * @param iv          iv向量
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = data} 	：明文数据
     */
    public UnionMessage servEA16(String keyValue, String keyType, int paddingFlag,
                                 String data, int exportFlag, int algorithmID, String iv) {
        if (UnionVerify.paramIsEmpty(keyValue)) {
            return UnionVerify.paramValueWrong("keyValue");
        }
        if (UnionVerify.paramIsEmpty(keyType)) {
            return UnionVerify.paramValueWrong("keyType");
        }
        if (paddingFlag != 0 && paddingFlag != 1) {
            return UnionVerify.paramValueWrong("paddingFlag");
        }
        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }
        if (exportFlag != 0 && exportFlag != 1) {
            return UnionVerify.paramValueWrong("exportFlag");
        }
        if (algorithmID != 0 && algorithmID != 1) {
            return UnionVerify.paramValueWrong("algorithmID");
        }


        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA16");
        um.putBodyField("keyValue", keyValue);
        um.putBodyField("keyType", keyType);
        um.putBodyField("paddingFlag", paddingFlag);
        um.putBodyField("data", data);
        um.putBodyField("exportFlag", exportFlag);
        um.putBodyField("algorithmID", algorithmID);
        if (algorithmID == 1) {
            um.putBodyField("iv", iv);
        }

        if (exportFlag == 0) {
            um.putBodyField("exportFlag", 1);
        } else {
            um.putBodyField("exportFlag", exportFlag);
        }

        um = UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
        //改变编码
        // Update by linxl 2017-09-26 解决网联服务输入数据为UTF-8的中文时，输出中文时会乱码
        if (UnionUtil.needToChangeEncodeUTF8(exportFlag + "", um.getHeadField("responseCode"))) {
            um = UnionUtil.changeEncodeUTF8(um, null, true);
        }
        return um;
    }

    /**
     * EA17	网联接入数字信封加密
     *
     * @param serialNum     证书序列号
     * @param mode          密钥模式
     * @param keyType       密钥类型
     * @param keyValue      密钥密文
     * @param data          明文数据
     * @param dataLen       数据长度
     * @param dataType      明文data数据类型
     * @param separator     分隔符
     * @param paddingFlag   填充标识
     * @param exportFlag    输出密文数据标识
     * @param keyByPKFormat 输出公钥加密的密钥密文编码格式
     * @param algorithmID   算法标识
     * @param iv            iv向量
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = keyByPK} 	：公钥加密的密钥密文<br>
     * {@code string = keyValue} 	：密钥密文<br>
     * {@code string = data} 	：密文数据
     */
    public UnionMessage servEA17(String serialNum, int mode, String keyType,
                                 String keyValue, String data, int dataLen, int dataType,
                                 String separator, int paddingFlag, int exportFlag,
                                 int keyByPKFormat, int algorithmID, String iv) {
        if (UnionVerify.paramIsEmpty(serialNum)) {
            return UnionVerify.paramValueWrong("serialNum");
        }

        if (mode != 0 && mode != 1) {
            return UnionVerify.paramValueWrong("mode");
        }

        if (UnionVerify.paramIsEmpty(keyType)) {
            return UnionVerify.paramValueWrong("keyType");
        }


        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        if (dataType != 0 && dataType != 1 && dataType != 3) {
            return UnionVerify.paramValueWrong("dataType");
        }

        if (paddingFlag != 0 && paddingFlag != 1) {
            return UnionVerify.paramValueWrong("paddingFlag");
        }

        if (exportFlag != 1 && exportFlag != 3) {
            return UnionVerify.paramValueWrong("exportFlag");
        }

        if (keyByPKFormat != 1 && keyByPKFormat != 3) {
            return UnionVerify.paramValueWrong("keyByPKFormat");
        }

        if (algorithmID != 0 && algorithmID != 1) {
            return UnionVerify.paramValueWrong("algorithmID");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA17");
        um.putBodyField("serialNum", serialNum);
        um.putBodyField("mode", mode);
        um.putBodyField("keyType", keyType);

        if (dataType == 0) {
            um.putBodyField("dataType", 1);
        } else {
            um.putBodyField("dataType", dataType);
        }
        um.putBodyField("data", data);
        um.putBodyField("dataLen", dataLen);
        um.putBodyField("separator", separator);
        um.putBodyField("paddingFlag", paddingFlag);
        um.putBodyField("exportFlag", exportFlag);
        um.putBodyField("keyByPKFormat", keyByPKFormat);
        um.putBodyField("algorithmID", algorithmID);
        if (mode == 1) {
            um.putBodyField("keyValue", keyValue);
        }
        if (!UnionVerify.paramIsEmpty(iv)) {
            um.putBodyField("iv", iv);
        }

        //改变编码
        // Update by linxl 2017-09-26 解决网联服务输入数据为UTF-8的中文时，输出中文时会乱码
        if (UnionUtil.needToChangeEncodeUTF8(dataType + "", null)) {
            um = UnionUtil.changeEncodeUTF8(um, separator, false);
        }

        return UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());
    }


    /**
     * EA18	网联接入数字信封解密
     *
     * @param serialNum     证书序列号
     * @param keyByPK       公钥加密的密钥密文
     * @param keyByPKFormat 公钥加密的密钥密文数据编码格式
     * @param data          密文数据
     * @param dataLen       数据长度
     * @param dataType      密文data数据类型
     * @param separator     分隔符
     * @param paddingFlag   填充标识
     * @param exportFlag    输出明文数据标识
     * @param algorithmID   算法标识
     * @param iv            iv向量
     * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
     * 获取响应信息：<br>
     * {@code string = keyValue} 	：密钥密文<br>
     * {@code string = keyType} 	：密钥类型<br>
     * {@code string = data} 	：明文数据
     */
    public UnionMessage servEA18(String serialNum, String keyByPK, int keyByPKFormat,
                                 String data, int dataLen, int dataType,
                                 String separator, int paddingFlag, int exportFlag,
                                 int algorithmID, String iv) {
        if (UnionVerify.paramIsEmpty(serialNum)) {
            return UnionVerify.paramValueWrong("serialNum");
        }

        if (UnionVerify.paramIsEmpty(keyByPK)) {
            return UnionVerify.paramValueWrong("keyByPK");
        }

        if (UnionVerify.paramIsEmpty(data)) {
            return UnionVerify.paramValueWrong("data");
        }

        if (dataType != 1 && dataType != 3) {
            return UnionVerify.paramValueWrong("dataType");
        }

        if (paddingFlag != 0 && paddingFlag != 1) {
            return UnionVerify.paramValueWrong("paddingFlag");
        }

        if (exportFlag != 0 && exportFlag != 1 && exportFlag != 3) {
            return UnionVerify.paramValueWrong("exportFlag");
        }

        if (algorithmID != 0 && algorithmID != 1) {
            return UnionVerify.paramValueWrong("algorithmID");
        }

        UnionMessage um = new UnionRequest();
        um.putHeadField("serviceCode", "EA18");
        um.putBodyField("serialNum", serialNum);
        um.putBodyField("keyByPK", keyByPK);
        um.putBodyField("keyByPKFormat", keyByPKFormat);

        if (exportFlag == 0) {
            um.putBodyField("exportFlag", 1);
        } else {
            um.putBodyField("exportFlag", exportFlag);
        }
        um.putBodyField("data", data);

        um.putBodyField("dataLen", dataLen);
        um.putBodyField("dataType", dataType);
        um.putBodyField("separator", separator);
        um.putBodyField("paddingFlag", paddingFlag);

        um.putBodyField("algorithmID", algorithmID);
        if (!UnionVerify.paramIsEmpty(iv)) {
            um.putBodyField("iv", iv);
        }

        um = UnionComm.sendAndRecvMsg(um, api.getMaxSendTimes());

        //改变编码
        // Update by linxl 2017-09-26 解决网联服务输入数据为UTF-8的中文时，输出中文时会乱码
        if (UnionUtil.needToChangeEncodeUTF8(exportFlag + "", um.getHeadField("responseCode"))) {
            um = UnionUtil.changeEncodeUTF8(um, separator, true);
        }
        return um;
    }

    /**
     * 文件解密,算法ECB,填充方式PKCS5
     * plainFile	明文文件名
     * cipherFile	密文文件名
     * keyValue		密钥值
     * keyType		密钥类型
     * mode			模式，0加密，1解密
     */
    private UnionMessage unionEncryptAndDecryptFile(String plainFile, String cipherFile,
                                                    String keyValue, String keyType, int mode) {
        if (UnionVerify.paramIsEmpty(plainFile)) {
            return UnionVerify.paramValueWrong("plainFile");
        }
        if (UnionVerify.paramIsEmpty(cipherFile)) {
            return UnionVerify.paramValueWrong("cipherFile");
        }
        if (UnionVerify.paramIsEmpty(keyValue)) {
            return UnionVerify.paramValueWrong("keyValue");
        }
        if (UnionVerify.paramIsEmpty(keyType)) {
            return UnionVerify.paramValueWrong("keyType");
        }

        // 读取文件到buffer中
        FileInputStream in = null;
        FileOutputStream out = null;
        UnionMessage um = null;

        try {
            if (mode == 1) {
                in = new FileInputStream(cipherFile);
                out = new FileOutputStream(plainFile);
            } else {
                in = new FileInputStream(plainFile);
                out = new FileOutputStream(cipherFile);
            }
            int peerLen = 512;
            byte[] inBuffer = new byte[peerLen];
            int totalLen = 0;
            totalLen = in.available();
            int num = 0;
            int curlen = 2048;
            int paddingFlag = 0;
            if (totalLen % peerLen != 0)
                num = totalLen / peerLen + 1;
            else
                num = totalLen / peerLen;
            for (int i = 0; i < num; i++) {
                if (i == num - 1) {
                    paddingFlag = 1;
                    if (totalLen % peerLen != 0) {
                        curlen = totalLen % peerLen;
                    }
                }
                final byte[] buffer = curlen < peerLen ? new byte[curlen] : inBuffer;
                in.read(buffer);
                String data = UnionStr.bcdhex_to_aschex(buffer);
                if (mode == 1) {
                    um = servEA16(keyValue, keyType, paddingFlag, data, 1, 0, null);
                } else {
                    um = servEA15(keyValue, keyType, paddingFlag, data, 1, 0, null);
                }
                if (!um.getHeadField("responseCode").equals("000000")) {
                    return um;
                }
                String odata = um.getBodyField("data");
                final byte[] outBuffer = UnionStr.aschex_to_bcdhex(odata);
                out.write(outBuffer);
            }
        } catch (Exception e) {
            return UnionVerify.paramValueWrong(e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return um;
    }

    /**
     * 文件解密,算法ECB,填充方式PKCS5
     * plainFile	明文文件名
     * cipherFile	密文文件名
     * keyValue		密钥值
     * keyType		密钥类型
     */
    public UnionMessage unionDecryptFile(String plainFile, String cipherFile,
                                         String keyValue, String keyType) {
        return unionEncryptAndDecryptFile(plainFile, cipherFile, keyValue, keyType, 1);
    }

    /**
     * 文件加密,算法ECB,填充方式PKCS5
     * plainFile	明文文件名
     * cipherFile	密文文件名
     * keyValue		密钥值
     * keyType		密钥类型
     */
    public UnionMessage unionEncryptFile(String plainFile, String cipherFile,
                                         String keyValue, String keyType) {
        return unionEncryptAndDecryptFile(plainFile, cipherFile, keyValue, keyType, 0);
    }

    /**
     * 文件解密,算法ECB,填充方式PKCS5
     * plainFile	明文文件名，如"D:/files/aa.txt"
     * cipherFile	密文文件名，如"D:/files/aa.enc"
     * serialNum	加密证书序列号
     * keyByPK		公钥加密的密钥密文，HEX数据
     * keyByPKFormat	公钥加密的密钥密文类型，1：hex数据，3：base64编码数据
     */
    public UnionMessage unionDecryptFileWithKeyByPK(String plainFile, String cipherFile,
                                                    String serialNum, String keyByPK, int keyByPKFormat) {
        UnionMessage um = null;
        String keyByPKHex = null;
        if (keyByPKFormat != 1 && keyByPKFormat != 3) {
            return UnionVerify.paramValueWrong("keyByPKFormat");
        }
        if (keyByPKFormat == 3) {
            try {
                final byte[] bcdK = Base64.getDecoder().decode(keyByPK);
                keyByPKHex = UnionStr.bcdhex_to_aschex(bcdK);
            } catch (Exception e) {
                return UnionVerify.paramValueWrong(e.getMessage());
            }
        } else {
            keyByPKHex = keyByPK;
        }
        um = servEA14(serialNum, keyByPKHex);
        if (!um.getHeadField("responseCode").equals("000000")) {
            return um;
        }
        String keyValue = um.getBodyField("keyValue");
        String keyType = um.getBodyField("keyType");

        um = unionEncryptAndDecryptFile(plainFile, cipherFile, keyValue, keyType, 1);
        return um;
    }
}
